<!-- 
    適用於BetterGI 0.51.0
    20250820新增了看死亡前最近的XY軸 
    20250821
        修正當有卡死與复活同時存在次數時，复活顯示不出XY軸 
        新增
            新增行高亮、鼠標移到表格行時高亮、提示當前在看哪一行
            按T鍵隱藏或顯示 开始时间&结束时间
            按Y鍵隱藏或顯示 复活次数、重试次数、疑似卡死次数、过远跳过、战斗超时、传送失败
            按U鍵切換表格格式（旧版/新版）
            按I鍵隱藏或顯示 拾取物列
            雙擊顯示數字的复战斗超时/疑似卡死次数/复活次数格時、重置該格生成的小視窗位置
            按P進入導出模式，顯示战斗超时/疑似卡死次数/复活次数小視窗詳細、方便分享
                導出模式中再次按P退出導出模式
            手機版可选择日志文件上傳
            当按下J键时，所有统计列（复活次数、重试次数、疑似卡死、过远跳过、战斗超时、传送失败）
                都为0或空的任务行 或 配置組 会被隐藏或显示，方便用户专注于有问题的任务。
    20250823
        导出模式快捷键支持：按P键进入导出模式后，现在支持使用Enter键确认操作，ESC键取消操作
        移动设备界面控制：新增Alt+L快捷键，可在电脑设备上显示/隐藏移动设备专用的上传功能区
        移动设备功能按钮：为移动设备添加专用功能按钮区，包含主题切换、列显示控制、导出模式等常用功能
    20250830
        捕获坐标信息修改：補充精确接近目标点
        修正暗色模式時、只有重试次数有數字時，任务名称不顯示紅色字體的問題
    20250831
        增加錯誤&其他訊息
            地图追踪战斗策略錯誤：".*"
            队伍缺少 X 元素採集角色
            队伍缺少纳西妲
            路徑文件丟失
            自动吃药
    20250905
        增加錯誤&其他訊息
            未匹配到任何战斗脚本
    20250906
        增加錯誤&其他訊息
            切换队伍失败，无法执行此路径！请检查配置组中的地图追踪配置！
    20250921
        增加錯誤&其他訊息
            当前获取焦点的窗口不是原神，尝试恢复窗口
            队伍角色识别失败
            打开大地图失败
        多次異常，顯示錯誤次數
            例如：快捷键暂停 (2)
    20250923
        增加錯誤&其他訊息
            选项列表不存在传送点  重试
            识别大地图位置失败
            切换区域失败
    20250925
        按H键，输入的任务名称关键字，根据任务名称匹配关键词来显示或隐藏整个任务
            多个关键词以","分割
        H键筛选模式時 与 J键隐藏异常为空的任务行時 不显示 所有拾取物
        增加錯誤&其他訊息
            执行地图追踪时候发生错误: "未能返回主界面"
        第二次讀檔時保留各功能狀態，優化用戶體驗
    20250927
        調整部份UI
            表头悬浮更貼近頂部
            功能按钮區域增加上下邊距
        修復摺疊或展開時，箭头顯示錯誤問題
        配置組增加顯示該組運行時隊伍角色
            隊伍角色與任務資訊始終顯示在頂部
            隊伍角色行在篩選模式與導出模式下置中顯示
    20250929
        增加快捷鍵
            快捷鍵6，計算下次60體力恢復時間
            快捷鍵8，計算下次指定體力恢復時間
        队伍角色行增加行走位、生存位資訊
            行走位、生存位資訊(僅供參考)
        調整部份UI
            配置組資訊始終顯示在頂部
    20250930
        优化配置组摺叠功能
            在配置组資訊固定在頂部時，點擊摺疊配置組，會自動將該配置組滾動到頂部位置
            确保在点击配置组标题时，被折叠的配置组始终滚动到页面最顶部位置。
        改善性能
            节流和防抖：对滚动和调整大小事件使用节流和防抖
            批量DOM操作：减少重排重绘
            缓存DOM查询：避免重复查询
            使用requestAnimationFrame：优化动画性能
            减少事件监听器：优化事件处理
            错误处理：添加try-catch防止崩溃
        筛选功能修改
            配置组名匹配 → 显示整个配置组
            配置组名不匹配但有任务匹配 → 显示配置组+匹配的任务
            配置组名和任务都不匹配 → 隐藏整个配置组
    20251001
        修正 筛选模式配置组名匹配時，沒有隱藏 整組都是0異常的配置組
        修正 筛选模式配置组名匹配時，舊版UI沒有隱藏0異常任務行的的拾取物
        條正 筛选模式時+只显示異常任務行，切換表格格式後沒有只显示 筛选關鍵字匹配的異常任務行
    20251003
        增加錯誤&其他訊息
            调度器任务出现未预期的异常，自动重启bgi
    20251006
        增加錯誤&其他訊息
        当前获取焦点的窗口不是原神，暂停
-->
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>日志分析工具</title>
    <style>
        html,
        body {
            background: #f8f9fa;
            color: #333;
            height: 100%;
            margin: 0;
            padding: 0;
            font-family: Arial, sans-serif;
        }

        #container {
            height: 100%;
            display: flex;
            flex-direction: column;
        }

        #dropZone {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 3px dashed #ccc;
            margin: 10px;
            transition: background-color 0.3s;
            overflow: auto;
        }

        #dropZone.dragover {
            background-color: #f0f0f0;
            border-color: #666;
        }

        #dropZone.empty {
            font-size: 24px;
            color: #666;
        }

        #dropZone.has-content {
            padding: 20px;
            align-items: flex-start;
        }

        table {
            border-collapse: collapse;
            width: 100%;
            margin: 10px 0;
        }

        th,
        td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }

        th {
            background-color: #888;
            position: sticky;
            /* top: 0; */
            top: -20px;
            /* 固定在页面顶部 */
            z-index: 1;
            /* 确保表头在内容上方 */
        }

        tr:nth-child(odd) {
            background-color: #eaeaea;
        }

        tr:nth-child(even) {
            background-color: #f9f9f9;
        }

        .red {
            color: red;
            font-weight: bold;
        }

        /* 新增折叠相关样式 */
        .group-header {
            cursor: pointer;
            display: flex;
            align-items: flex-start;
            /* 改为flex-start确保内容顶部对齐 */
            padding: 10px;
            background-color: #e7e7e7;
            border-radius: 4px;
            margin: 5px 0;
        }

        .group-header:hover {
            background-color: #bdbebf;
        }

        /* 修改箭头相关样式 */
        .arrow {
            display: inline-block;
            margin-right: 10px;
            /* margin-top: 25px; */
            margin-top: 12.5px;
            /* 与标题文字对齐 */
            transition: transform 0.2s;
            transform: rotate(0deg);
            /* 默认向右（折叠状态） */
        }

        .arrow.collapsed {
            transform: rotate(90deg);
            /* 展开时向下 */
        }

        .group-container {
            margin-left: 20px;
            border-left: 2px solid #dee2e6;
            padding-left: 10px;
            margin-bottom: 0;
        }

        .collapsible {
            display: block;
        }

        .collapsible.collapsed {
            display: none;
        }

        /* 暗色主题样式 */
        body.dark-theme {
            background-color: #121212;
            color: #ffffff;
        }

        body.dark-theme #dropZone {
            border-color: #666;
            background-color: #1e1e1e;
        }

        body.dark-theme table {
            border-color: #444;
        }

        body.dark-theme th,
        body.dark-theme td {
            border-color: #444;
        }

        body.dark-theme th {
            background-color: #888;
        }

        body.dark-theme tr:nth-child(odd) {
            background-color: #2a2a2a;
        }

        body.dark-theme tr:nth-child(even) {
            background-color: #1e1e1e;
        }

        body.dark-theme .group-header {
            background-color: #333;
            color: #ffffff;
        }

        body.dark-theme .group-header:hover {
            background-color: #444;
        }

        body.dark-theme .red {
            color: #ff6666;
        }

        /* 弹窗样式 */
        .coord-popup {
            display: none;
            position: fixed;
            background-color: #f9f9f9;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
            z-index: 10000;
            max-width: 250px;
            font-size: 14px;
            cursor: default;
            user-select: text;
        }

        .coord-popup.visible {
            display: block;
        }

        .coord-popup-header {
            background-color: #eee;
            padding: 8px 12px;
            border-bottom: 1px solid #ccc;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .coord-popup-title {
            font-weight: bold;
        }

        .coord-popup .close-btn {
            cursor: pointer;
            font-size: 18px;
            color: #999;
            padding: 0 5px;
        }

        .coord-popup .close-btn:hover {
            color: #000;
        }

        .coord-popup-content {
            padding: 10px;
            max-height: 300px;
            overflow-y: auto;
        }

        .coord-popup .coord-item {
            margin: 5px 0;
            padding: 3px 0;
            border-bottom: 1px dashed #ddd;
            cursor: text;
        }

        .coord-popup .coord-item:last-child {
            border-bottom: none;
        }

        /* 暗色主题下的弹窗样式 */
        body.dark-theme .coord-popup {
            background-color: #333;
            border: 1px solid #666;
            color: #fff;
        }

        body.dark-theme .coord-popup-header {
            background-color: #444;
            border-bottom: 1px solid #555;
        }

        body.dark-theme .coord-popup .close-btn {
            color: #ccc;
        }

        body.dark-theme .coord-popup .close-btn:hover {
            color: #fff;
        }

        body.dark-theme .coord-popup .coord-item {
            border-bottom: 1px dashed #555;
        }

        /* 单元格固定状态指示器 */
        .coords-cell.pinned::after {
            content: "📌";
            margin-left: 5px;
            font-size: 12px;
        }

        /* 修改单元格光标样式 */
        .coords-cell {
            position: relative;
            /* 默认使用箭头光标 */
            cursor: default;
        }

        /* 当单元格有数字时使用手指光标 */
        .coords-cell.has-coords {
            cursor: pointer;
        }

        /* 新增提示文本样式 */
        .dispatch-alert {
            color: orange;
            font-weight: bold;
        }

        .pause-alert {
            color: rgb(255, 0, 255);
            font-weight: bold;
        }

        .abnormal-alert {
            color: red;
            font-weight: bold;
        }

        .strategy-error {
            color: red;
            /* 战斗策略错误 */
            font-weight: bold;
        }

        .element-missing {
            color: red;
            /* 缺少元素采集角色 */
            font-weight: bold;
        }

        .nahida-missing {
            color: red;
            /* 缺少纳西妲 */
            font-weight: bold;
        }

        .file-missing {
            color: red;
            /* 文件丢失 */
            font-weight: bold;
        }

        .auto-potion {
            color: #32CD32;
            /* 自动吃药 */
            font-weight: bold;
        }

        /* 在CSS样式中添加战斗脚本错误样式 */
        .combat-script-error {
            color: red;
            /* 战斗脚本设置错误 */
            font-weight: bold;
        }

        /* 新增红色任务名称样式 */
        .task-name-red {
            color: red;
            font-weight: bold;
        }

        .duration-red {
            color: red;
            font-weight: bold;
        }

        /* 新增弹窗样式 */
        .battle-popup {
            display: none;
            position: fixed;
            background-color: #f9f9f9;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
            z-index: 10000;
            max-width: 250px;
            max-height: 400px;
            font-size: 14px;
            cursor: default;
            user-select: text;
            overflow: auto;
        }

        .battle-popup.visible {
            display: block;
        }

        .battle-popup-header {
            background-color: #eee;
            padding: 8px 12px;
            border-bottom: 1px solid #ccc;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
            position: sticky;
            top: 0;
        }

        .battle-popup-title {
            font-weight: bold;
        }

        .battle-popup .close-btn {
            cursor: pointer;
            font-size: 18px;
            color: #999;
            padding: 0 5px;
        }

        .battle-popup .close-btn:hover {
            color: #000;
        }

        .battle-popup-content {
            padding: 10px;
            max-height: 300px;
            overflow-y: auto;
        }

        .battle-popup .battle-item {
            margin: 5px 0;
            padding: 3px 0;
            border-bottom: 1px dashed #ddd;
            font-family: monospace;
        }

        .battle-popup .battle-item:last-child {
            border-bottom: none;
        }

        /* 暗色主题下的弹窗样式 */
        body.dark-theme .battle-popup {
            background-color: #333;
            border: 1px solid #666;
            color: #fff;
        }

        body.dark-theme .battle-popup-header {
            background-color: #444;
            border-bottom: 1px solid #555;
        }

        body.dark-theme .battle-popup .close-btn {
            color: #ccc;
        }

        body.dark-theme .battle-popup .close-btn:hover {
            color: #fff;
        }

        body.dark-theme .battle-popup .battle-item {
            border-bottom: 1px dashed #555;
        }

        /* 单元格样式 */
        .has-battle-events {
            cursor: pointer;
            position: relative;
        }

        .has-battle-events.pinned::after {
            content: "📌";
            margin-left: 5px;
            font-size: 12px;
        }

        /* 无事件时不可点击 */
        .no-events {
            cursor: default !important;
        }

        /* 新增：时间列隐藏状态样式 */
        .time-columns-hidden .column-start-time,
        .time-columns-hidden .column-end-time {
            display: none;
        }

        /* 新增：时间列显示状态指示器 */
        .time-toggle-indicator {
            position: fixed;
            bottom: 10px;
            right: 10px;
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 12px;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.5s;
        }

        .time-toggle-indicator.visible {
            opacity: 1;
        }

        /* 新增：统计列隐藏状态样式 */
        .stats-columns-hidden .column-revive,
        .stats-columns-hidden .column-retry,
        .stats-columns-hidden .column-retry-detail,
        .stats-columns-hidden .column-too-far-skip,
        .stats-columns-hidden .column-timeout,
        .stats-columns-hidden .column-teleport {
            display: none;
        }

        /* 新增：统计列显示状态指示器 */
        .stats-toggle-indicator {
            position: fixed;
            bottom: 60px;
            right: 10px;
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 5px 10px;
            border-radius: 5px;
            font-size: 12px;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.5s;
        }

        .legacy-format .group-header {
            /* 移除sticky定位 */
            position: static;
            background-color: #e7e7e7;
            padding: 10px;
            margin: 5px 0;
        }

        body.dark-theme .legacy-format .group-header {
            background-color: #333;
        }

        /* 确保旧版格式下表头位置正确 */
        .legacy-format .sticky-header {
            top: 40px;
        }

        /* 對於舊版格式的特殊調整 */
        .legacy-format .sticky-header {
            top: 40px;
        }

        .legacy-format .main-row-name {
            /* 移除粗体 */
            font-weight: normal;
            background-color: #f0f0f0;
        }

        .legacy-format .sub-row {
            background-color: #f9f9f9;
        }

        .legacy-format .ignore-sort {
            background-color: #e0e0e0;
        }

        .ignore-sort {
            font-weight: normal;
        }

        /* 暗色主题下的新版表格样式 */
        body.dark-theme .legacy-format .main-row-name {
            background-color: #2a2a2a;
        }

        body.dark-theme .legacy-format .sub-row {
            background-color: #1e1e1e;
        }

        body.dark-theme .legacy-format .ignore-sort {
            background-color: #333;
        }

        /* 标题跟随页面功能 */
        .sticky-title {
            position: sticky;
            top: 0;
            background-color: #e7e7e7;
            z-index: 100;
            padding: 10px;
            margin: -10px -10px 10px -10px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        body.dark-theme .sticky-title {
            background-color: #1e1e1e;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
        }

        /* 拾取物粗体样式 */
        .picks-bold {
            font-weight: bold;
        }

        /* 行高亮样式 */
        tr.highlighted {
            background-color: #e6f7ff !important;
            /* 白色模式下的淡蓝色 */
        }

        body.dark-theme tr.highlighted {
            background-color: #3a3a3a !important;
            /* 黑色模式下的中灰色 */
        }

        /* 新增：帮助提示样式 */
        .help-indicator {
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 10px 15px;
            border-radius: 8px;
            font-size: 14px;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.5s;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
        }

        .help-indicator.visible {
            opacity: 1;
        }

        /* 默认亮色主题下的帮助提示样式 */
        .help-indicator {
            background-color: #000;
            color: #fff;
        }

        /* 暗色主题下的帮助提示样式 */
        body.dark-theme .help-indicator {
            background-color: #fff;
            color: #000;
        }

        /* 新增：拾取物列隐藏状态样式 */
        .picks-columns-hidden .column-picks {
            display: none;
        }

        /* 调整指示器位置 */
        .stats-toggle-indicator#picksToggleIndicator {
            bottom: 60px;
        }

        .mode-indicator {
            position: fixed;
            top: 10px;
            left: 10px;
            background-color: rgba(76, 175, 80, 0.8);
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 14px;
            z-index: 10000;
            display: none;
        }

        .static-mode .mode-indicator {
            display: block;
            background-color: rgba(76, 175, 80, 0.8);
        }

        /* 静态模式下弹窗的样式 */
        .static-mode .coord-popup,
        .static-mode .battle-popup {
            display: block !important;
            position: static !important;
            margin: 10px 0;
            box-shadow: none;
            max-width: 100%;
            max-height: none;
        }

        .static-mode .coord-popup-header,
        .static-mode .battle-popup-header {
            cursor: default;
            background-color: #e0e0e0;
            /* color: #333; */
        }

        .static-mode .close-btn {
            display: none;
        }

        .static-mode .coords-cell,
        .static-mode .has-battle-events {
            cursor: default;
        }

        .static-mode .coords-cell.pinned::after,
        .static-mode .has-battle-events.pinned::after {
            content: "";
        }

        /* 当筛选提示显示时，静态模式提示向右移动更多 */
        .filter-indicator.visible~.mode-indicator.static-mode {
            left: calc(var(--filter-indicator-width) + 20px);
        }

        /* 提示框样式 */
        .custom-alert {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            z-index: 10000;
            min-width: 300px;
            text-align: center;
            display: none;
        }

        .custom-alert h3 {
            margin-top: 0;
            color: #333;
        }

        .custom-alert-buttons {
            display: flex;
            justify-content: center;
            gap: 10px;
            margin-top: 20px;
        }

        .custom-alert-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }

        .custom-alert-confirm {
            background-color: #4CAF50;
            color: white;
        }

        .custom-alert-cancel {
            background-color: #f44336;
            color: white;
        }

        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 9999;
            display: none;
        }

        body.dark-theme .custom-alert {
            background-color: #333;
            color: #fff;
        }

        body.dark-theme .custom-alert h3 {
            color: #fff;
        }

        /* 新增移动设备上传按钮样式 */
        #fileUploadContainer {
            display: flex;
            flex-direction: column;
            gap: 15px;
            padding: 15px;
            border: 2px dashed #ccc;
            border-radius: 8px;
            background-color: #f9f9f9;
            margin: 10px;
            align-items: center;
        }

        #fileUploadBtn {
            padding: 12px 24px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
            width: 100%;
            max-width: 300px;
        }

        #fileUploadBtn:hover {
            background-color: #45a049;
        }

        /* 功能按钮区域 */
        #mobileControls {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            margin: 8px 0px;
            gap: 8px;
            width: 100%;
        }

        .control-btn {
            padding: 8px 12px;
            border: 1px solid #ccc;
            border-radius: 4px;
            background-color: #f0f0f0;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s;
            flex: 1;
            min-width: 80px;
            text-align: center;
        }

        .control-btn.active {
            background-color: #4CAF50;
            color: white;
            border-color: #4CAF50;
            font-weight: bold;
        }


        .control-btn:nth-child(1).active {
            background-color: #ff9800;
            border-color: #ff9800;
        }

        /* 主题切换 */
        .control-btn:nth-child(2).active {
            background-color: #2196F3;
            border-color: #2196F3;
        }

        /* 时间列 */
        .control-btn:nth-child(3).active {
            background-color: #2196F3;
            border-color: #2196F3;
        }

        /* 统计列 */
        .control-btn:nth-child(4).active {
            background-color: #2196F3;
            border-color: #2196F3;
        }

        /* 拾取物列 */
        .control-btn:nth-child(5).active {
            background-color: #9C27B0;
            border-color: #9C27B0;
        }

        /* 导出模式 */
        .control-btn:nth-child(6).active {
            background-color: #f44336;
            border-color: #f44336;
        }

        /* 异常任务 */



        #fileInput {
            display: none;
        }

        /* 暗色主题适配 */
        body.dark-theme #fileUploadContainer {
            background-color: #2a2a2a;
            border-color: #555;
        }

        body.dark-theme .control-btn {
            background-color: #333;
            color: #fff;
            border-color: #555;
        }

        /* 移动设备检测 */
        @media (max-width: 0px),
        (pointer: coarse) {
            #fileUploadContainer {
                display: flex !important;
            }
        }

        /* 电脑设备默认隐藏 */
        #fileUploadContainer {
            display: none;
        }

        /* 电脑设备显示控制 */
        #fileUploadContainer.visible {
            display: flex;
        }

        /* 新增：移动设备功能按钮响应式样式 */
        @media (max-width: 768px),
        (pointer: coarse) {
            #mobileControls {
                flex-wrap: wrap;
                max-height: 88px;
                /* 限制最多两行高度 */
                overflow: hidden;
                gap: 4px;
                /* 减小按钮间距 */
            }

            .control-btn {
                flex: 0 0 calc(24% - 6px);
                /* 每行显示2个按钮，考虑间隙 */
                min-width: calc(24% - 6px);
                font-size: 12px;
                /* 减小字体大小 */
                padding: 6px 8px;
                /* 调整内边距 */
                margin: 2px;
                /* 减小外边距 */
            }

            /* 确保按钮文本不会换行 */
            .control-btn {
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
        }

        /* 超小屏幕优化（宽度小于400px） */
        @media (max-width: 400px) {
            #mobileControls {
                max-height: 80px;
                /* 进一步减小高度 */
                gap: 2px;
            }

            .control-btn {
                flex: 0 0 calc(24% - 4px);
                min-width: calc(24% - 4px);
                font-size: 11px;
                padding: 4px 6px;
            }
        }

        /* 新增移动设备样式 */
        @media (max-width: 0px),
        (pointer: coarse) {
            body {
                text-size-adjust: none;
                -webkit-text-size-adjust: none;
            }

            .task-name-red,
            .main-row-name,
            .group-header h2 {
                font-size: 16px;
                line-height: 1.4;
                text-size-adjust: none;
                -webkit-text-size-adjust: none;
            }

            table {
                font-size: 14px;
            }

            th,
            td {
                padding: 6px 4px;
            }

            /* 移动设备上传按钮样式 */
            #fileUploadContainer {
                display: block;
                text-align: center;
                margin: 15px;
                padding: 15px;
                border: 2px dashed #ccc;
                border-radius: 8px;
                background-color: #f9f9f9;
            }

            #fileUploadBtn {
                padding: 12px 24px;
                background-color: #4CAF50;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 16px;
                transition: background-color 0.3s;
            }

            #fileUploadBtn:hover {
                background-color: #45a049;
            }

            #fileInput {
                display: none;
            }
        }

        /* 非移动设备默认隐藏上传区域 */
        #fileUploadContainer {
            display: none;
            /* 默认隐藏 */
            text-align: center;
            margin-top: 20px;
            padding: 15px;
            border: 2px dashed #ccc;
            border-radius: 8px;
            background-color: #f9f9f9;
        }

        /* 添加零统计任务显示状态指示器样式 */
        .stats-toggle-indicator#zeroStatsToggleIndicator {
            bottom: 160px;
        }

        /* 新增：上传区域显示控制 */
        #fileUploadContainer.hidden {
            display: none !important;
        }

        /* 移动设备样式 */
        @media (max-width: 0px),
        (pointer: coarse) {
            #fileUploadContainer {
                display: block !important;
                /* 移动设备强制显示 */
            }
        }

        /* 电脑设备显示时的样式 */
        #fileUploadContainer.visible {
            display: block;
        }

        /* 暗色主题适配 */
        body.dark-theme #fileUploadContainer {
            background-color: #2a2a2a;
            border-color: #555;
        }

        /* 新增：篩選狀態提示樣式 */
        .filter-indicator {
            position: fixed;
            top: 10px;
            left: 10px;
            background-color: rgba(255, 165, 0, 0.9);
            color: white;
            padding: 8px 12px;
            border-radius: 4px;
            font-size: 14px;
            z-index: 10000;
            display: none;
            max-width: 300px;
            word-wrap: break-word;
        }

        .filter-indicator.visible {
            display: block;
        }

        .filter-indicator .filter-keywords {
            font-weight: bold;
            color: #ffeb3b;
        }

        /* 暗色主题适配 */
        body.dark-theme .filter-indicator {
            background-color: rgba(255, 140, 0, 0.9);
            color: #fff;
        }

        body.dark-theme .filter-indicator .filter-keywords {
            color: #fff59d;
        }

        body.dark-theme .mode-indicator {
            background-color: rgba(56, 142, 60, 0.8);
        }

        /* 任務篩選 */
        .control-btn:nth-child(7).active {
            background-color: #FF9800;
            border-color: #FF9800;
        }

        .header-content {
            flex: 1;
            /* 占据剩余空间 */
            display: flex;
            flex-direction: column;
            /* 垂直排列 */
        }

        /* 队伍角色显示样式 */
        .team-roles {
            margin-top: -20px;
            padding: 8px;
            /* background-color: #f8f8f8; */
            border-radius: 4px;
            /* border-left: 3px solid #4CAF50; */
            font-size: 14px;
        }

        .team-role {
            /* margin: 3px 0; */
            /* padding: 2px 5px; */
            font-family: monospace;
            color: #555;
        }

        body.dark-theme .team-role {
            color: #ccc;
        }

        /* 隊伍角色行樣式 - 修改後 */
        .team-roles-row {
            background-color: #f0f0f0 !important;
            border-bottom: 1px solid #ddd;
            position: sticky;
            top: -20px;
            z-index: 4;
            font-size: 14px;
        }

        /* 修正暗色模式下队伍角色行的背景色 */
        body.dark-theme .team-roles-row {
            background-color: #888 !important;
            /* border-bottom-color: #444; */
            color: #ffffff;
        }

        /* 确保队伍角色行的颜色与任务名称一致 */
        .team-roles-content {
            /* padding: 8px; */
            font-family: monospace;
            color: #333;
            font-weight: bolder;
            /* 亮色模式下的字体颜色，与任务名称一致 */
        }

        .team-role-line {
            /* margin: 3px 0; */
            /* padding: 2px 0; */
            line-height: 1.4;
        }

        body.dark-theme .team-role-line {
            color: #ffffff;
        }

        /* 调整表头位置，为队伍角色行留出空间 */
        .sticky-header {
            position: sticky;
            top: 40px;
            /* 根据队伍角色行高度调整 */
            z-index: 3;
        }



        /* 对于旧版格式的特殊调整 */
        .legacy-format .team-roles-row {
            top: -20px;
            /* 统一为0px */
        }

        body.dark-theme .team-roles-content {
            color: #ffffff;
            /* 暗色模式下的字体颜色，与任务名称一致 */
        }

        /* 确保任务信息行在队伍角色行之下 */
        .group-container table {
            position: relative;
            /* 移除可能的额外边距 */
        }

        /* 隊伍角色行樣式 - 修改後 */
        .team-roles-row {
            background-color: #888 !important;
            border-bottom: 1px solid #ddd;
            position: sticky;
            top: -20px;
            /* 固定在頂部，在表頭上方 */
            z-index: 4;
            /* 確保在表頭上方 */
            font-size: 14px;
        }

        /* 确保配置组标题不会覆盖队伍角色行 */
        .group-header {
            position: relative;
            z-index: 5;
            /* 高于队伍角色行 */
        }

        /* 表头样式 */
        th.sticky-header {
            position: sticky;
            top: 20px;
            /* 初始值為0，由JS動態設置 */
            z-index: 3;
        }

        /* 当body有static-mode或filter-mode类时，队伍角色行居中 */
        body.static-mode .team-role-line,
        body.filter-mode .team-role-line {
            text-align: center;
        }

        /* 默认状态 */
        .team-role-line {
            text-align: left;
        }

        /* 配置组标题固定样式 */
        .group-header {
            position: sticky;
            top: -20px;
            z-index: 20;
            background-color: #e7e7e7;
            margin: 5px 0;
            transition: background-color 0.3s;
        }

        /* 暗色主题下的配置组标题固定样式 */
        body.dark-theme .group-header {
            background-color: #333;
        }

        /* 确保配置组容器在折叠状态下不会占用空间 */
        .group-container.collapsed {
            display: none;
        }

        /* 确保配置组标题在折叠状态下仍然可见但内容隐藏 */
        .group-header.collapsed {
            position: sticky;
            top: -20px;
            z-index: 20;
        }

        /* 调整表头位置，确保在配置组标题下方 */
        .sticky-header {
            position: sticky;
            /* top: 60px; */
            top: 40px;
            /* 根据配置组标题高度调整 */
            z-index: 3;
        }

        /* 队伍角色行位置调整 */
        .team-roles-row {
            position: sticky;
            /* top: 40px; */
            top: 20px;
            /* 在配置组标题下方，表头上方 */
            z-index: 4;
        }

        /* 对于旧版格式的特殊调整 */
        .legacy-format .sticky-header {
            /* top: 60px; */
            top: 40px;
        }

        .legacy-format .team-roles-row {
            /* top: 40px; */
            top: 40px;
        }

        /* 确保配置组标题有足够的视觉层次 */
        .group-header h2 {
            margin: 0;
            padding: 10px 0;
            font-size: 18px;
        }

        /* 为配置组容器添加顶部内边距，确保内容不被固定标题遮挡 */
        .group-container {
            margin-left: 20px;
            border-left: 2px solid #dee2e6;
            padding-left: 10px;
            position: relative;
        }

        /* 当有多个配置组时，确保后续配置组标题能够正确推走前一个 */
        .group-container:not(:first-child) .group-header {
            top: -20px;
            /* 所有配置组标题都固定在页面顶部，滚动时会依次替换 */
        }

        /* 为配置组标题添加阴影，增强视觉层次 */
        .group-header {
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            border-radius: 4px;
        }

        body.dark-theme .group-header {
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }

        /* 旧版格式的配置组标题固定样式 */
        .legacy-format .group-header {
            position: sticky;
            top: -20px;
            z-index: 20;
            background-color: #e7e7e7;
            margin: 5px 0;
            transition: background-color 0.3s;
        }

        /* 暗色主题下的旧版格式配置组标题固定样式 */
        body.dark-theme .legacy-format .group-header {
            background-color: #333;
        }

        /* 确保旧版格式配置组标题在折叠状态下也保持固定 */
        .legacy-format .group-header.collapsed {
            position: sticky;
            top: -20px;
            z-index: 20;
        }

        /* 调整旧版格式表头位置，确保在配置组标题下方 */
        .legacy-format .sticky-header {
            position: sticky;
            /* top: 60px; */
            top: 40px;
            /* 根据配置组标题高度调整 */
            z-index: 3;
        }

        /* 旧版格式队伍角色行位置调整 */
        .legacy-format .team-roles-row {
            position: sticky;
            top: 20px;
            /* 在配置组标题下方，表头上方 */
            z-index: 4;
        }

        /* 为旧版格式配置组标题添加阴影，增强视觉层次 */
        .legacy-format .group-header {
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            border-radius: 4px;
        }

        body.dark-theme .legacy-format .group-header {
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }

        /* 确保旧版格式配置组容器有足够的顶部内边距 */
        .legacy-format .group-container {
            margin-left: 20px;
            border-left: 2px solid #dee2e6;
            padding-left: 10px;
            position: relative;
        }
    </style>
</head>

<body>
    <!-- 新增：篩選狀態提示 -->
    <div class="filter-indicator" id="filterIndicator">
        <span>正在篩選: </span>
        <span class="filter-keywords" id="filterKeywords"></span>
        <br><small>按 H 鍵退出篩選</small>
    </div>
    <div class="mode-indicator">静态模式 <br><small>请 另存新档 或 截圖 分享页面</small></div>

    <div id="container">
        <div id="dropZone" class="empty">将日志文件拖放到此处</div>
        <!-- 修改后的上传区域 -->
        <div id="fileUploadContainer">
            <!-- 上层：选择文件按钮 -->
            <div>
                <input type="file" id="fileInput" accept=".log,.txt" multiple>
                <button id="fileUploadBtn">选择日志文件</button>
            </div>

            <!-- 下层：功能按钮 -->
            <div id="mobileControls">
                <button class="control-btn" data-key="192">切换颜色</button>
                <button class="control-btn" data-key="84">时间列</button>
                <button class="control-btn" data-key="89">统计列</button>
                <button class="control-btn" data-key="73">拾取物列</button>
                <button class="control-btn" data-key="80">导出模式</button>
                <button class="control-btn" data-key="74">异常任务</button>
                <button class="control-btn" data-key="72">任務篩選</button>
            </div>
        </div>
    </div>

    <!-- 自定义提示框 -->
    <div class="overlay" id="alertOverlay"></div>
    <div class="custom-alert" id="customAlert">
        <h3 id="alertTitle">确认操作</h3>
        <p id="alertMessage">您确定要执行此操作吗？</p>
        <div class="custom-alert-buttons">
            <button class="custom-alert-btn custom-alert-confirm" id="alertConfirm">确定</button>
            <button class="custom-alert-btn custom-alert-cancel" id="alertCancel">取消</button>
        </div>
    </div>

    <!-- 新增：时间列显示状态指示器 -->
    <div class="time-toggle-indicator" id="timeToggleIndicator"></div>
    <!-- 新增：统计列显示状态指示器 -->
    <div class="stats-toggle-indicator" id="statsToggleIndicator"></div>
    <!-- 新增：表格格式显示状态指示器 -->
    <div class="stats-toggle-indicator" id="formatToggleIndicator" style="bottom: 110px;"></div>
    <!-- 在HTML中添加拾取物列显示状态指示器 -->
    <div class="stats-toggle-indicator" id="picksToggleIndicator" style="bottom: 60px;"></div>
    <!-- 在HTML中添加零统计任务显示状态指示器 -->
    <div class="stats-toggle-indicator" id="zeroStatsToggleIndicator" style="bottom: 160px;"></div>
    <!-- 新增：帮助提示指示器 -->
    <div class="help-indicator" id="helpIndicator">按 0 键查看说明</div>

    <script>
        // 全局变量
        let lastValidTime = null;
        let timeColumnsVisible = true; // 跟踪时间列的显示状态
        let statsColumnsVisible = true; // 跟踪统计列的显示状态
        let useLegacyFormat = false; // 跟踪表格格式状态
        let picksColumnsVisible = true; // 跟踪拾取物列的显示状态
        let hasLogData = false; // 跟踪是否有日志数据
        let hideZeroStatsTasks = false; // 跟踪是否隐藏所有统计为0的任务
        let isStaticMode = false;
        // 添加上传区域显示状态变量
        let uploadContainerVisible = false; // 电脑设备上上传区域的显示状态

        let controlButtons = {}; // 存储按钮状态
        let isFilterMode = false; // 跟踪篩選模式狀態
        let currentFilterKeywords = []; // 當前篩選關鍵字

        let isAdjustingTableHeader = false;
        let lastAdjustmentTime = 0;
        const ADJUSTMENT_THROTTLE = 100; // 100ms节流

        // 自定义确认对话框
        function showCustomConfirm(title, message) {
            return new Promise((resolve) => {
                const alert = document.getElementById('customAlert');
                const overlay = document.getElementById('alertOverlay');
                const titleEl = document.getElementById('alertTitle');
                const messageEl = document.getElementById('alertMessage');
                const confirmBtn = document.getElementById('alertConfirm');
                const cancelBtn = document.getElementById('alertCancel');

                // 设置标题和消息
                titleEl.textContent = title;
                messageEl.textContent = message;

                // 显示对话框
                alert.style.display = 'block';
                overlay.style.display = 'block';

                // 键盘事件处理函数
                const handleKeyPress = (e) => {
                    if (e.key === 'Enter') {
                        onConfirm();
                    } else if (e.key === 'Escape') {
                        onCancel();
                    }
                };

                // 处理确认按钮点击
                const onConfirm = () => {
                    cleanup();
                    resolve(true);
                };

                // 处理取消按钮点击
                const onCancel = () => {
                    cleanup();
                    resolve(false);
                };

                // 清理事件监听器
                const cleanup = () => {
                    confirmBtn.removeEventListener('click', onConfirm);
                    cancelBtn.removeEventListener('click', onCancel);
                    overlay.removeEventListener('click', onCancel);
                    document.removeEventListener('keydown', handleKeyPress);
                    alert.style.display = 'none';
                    overlay.style.display = 'none';
                };

                // 添加事件监听器
                confirmBtn.addEventListener('click', onConfirm);
                cancelBtn.addEventListener('click', onCancel);
                overlay.addEventListener('click', onCancel);
                document.addEventListener('keydown', handleKeyPress);
            });
        }

        // 导出为静态HTML
        function exportAsStaticHTML() {
            // 打开所有弹窗并将其内容内联显示
            document.querySelectorAll('.coord-popup, .battle-popup').forEach(popup => {
                popup.style.display = 'block';
                popup.style.position = 'static';
                popup.style.boxShadow = 'none';
                popup.style.margin = '10px 0';
                popup.style.maxWidth = '100%';
                popup.style.maxHeight = 'none';

                // 移除关闭按钮
                const closeBtn = popup.querySelector('.close-btn');
                if (closeBtn) closeBtn.style.display = 'none';
            });

            // 禁用所有交互元素
            document.querySelectorAll('.coords-cell, .has-battle-events').forEach(cell => {
                cell.style.cursor = 'default';
            });

            // 添加静态模式类
            document.body.classList.add('static-mode');
            const modeIndicator = document.querySelector('.mode-indicator');
            if (modeIndicator) {
                modeIndicator.classList.add('static-mode');
            }
            isStaticMode = true;

            // 添加static-mode类到body
            document.body.classList.add('static-mode');

            // 显示提示信息
            updateButtonState(80);
            // alert("页面已转换为静态模式，请使用浏览器的\"另存新档\"功能(Ctrl+S)保存网页。");
        }

        // 恢复交互模式
        function restoreInteractiveMode() {
            // 隐藏所有弹窗
            document.querySelectorAll('.coord-popup, .battle-popup').forEach(popup => {
                popup.style.display = '';
                popup.style.position = '';
                popup.style.boxShadow = '';
                popup.style.margin = '';
                popup.style.maxWidth = '';
                popup.style.maxHeight = '';

                // 恢复关闭按钮
                const closeBtn = popup.querySelector('.close-btn');
                if (closeBtn) closeBtn.style.display = '';
            });

            // 恢复交互元素
            document.querySelectorAll('.coords-cell.has-coords, .has-battle-events').forEach(cell => {
                cell.style.cursor = 'pointer';
            });

            // 移除静态模式类
            document.body.classList.remove('static-mode');
            const modeIndicator = document.querySelector('.mode-indicator');
            if (modeIndicator) {
                modeIndicator.classList.remove('static-mode');
            }
            isStaticMode = false;

            // 移除static-mode类
            document.body.classList.remove('static-mode');

            // 重新初始化弹窗
            if (window.setupCoordPopups) {
                setupCoordPopups();
            }
        }

        // 处理P键按下事件
        function handlePKey() {
            if (isStaticMode) {
                // 静态模式下直接恢复交互模式
                restoreInteractiveMode();
            } else {
                // 交互模式下询问用户是否导出
                showCustomConfirm(
                    "导出静态报告",
                    `是否显示卡死点并转换为静态模式以便导出文件？转换为静态模式后，请使用浏览器的\"另存新档\"功能(Ctrl+S)分享网页。或截图分享当前页面。`

                ).then(confirmed => {
                    if (confirmed) {
                        exportAsStaticHTML();
                        // 注意：exportAsStaticHTML()内部已经添加了static-mode类
                    }
                });
            }
        }

        // 在文件解析前重置时间戳追踪
        function resetTimeTracker() {
            lastValidTime = null;
        }
        const parsingContext = {
            activeGroups: new Map(),
            activeTasks: new Map(),
            allGroups: []
        };

        // 页面加载时显示帮助提示
        window.onload = function () {
            const now = new Date();
            const currentHour = now.getHours();
            const minutes = now.getMinutes();
            if ((currentHour < 6) || (currentHour === 6 && minutes <= 30)) {
                document.body.classList.add('dark-theme');
            }

            // 显示帮助提示
            const helpIndicator = document.getElementById('helpIndicator');
            helpIndicator.classList.add('visible');

            // 5秒后隐藏帮助提示
            setTimeout(() => {
                helpIndicator.classList.remove('visible');
            }, 5000);

            // 设置文件上传按钮事件
            document.getElementById('fileUploadBtn').addEventListener('click', function () {
                document.getElementById('fileInput').click();
            });

            // 处理文件选择事件
            document.getElementById('fileInput').addEventListener('change', handleFileSelect);

            // 检测是否为移动设备
            const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
            const hasTouchScreen = 'ontouchstart' in window || navigator.maxTouchPoints > 0;

            // 如果是移动设备，添加移动设备类并显示文件上传区域
            if (isMobileDevice || hasTouchScreen) {
                document.body.classList.add('mobile-device');
                document.getElementById('fileUploadContainer').classList.add('visible');
            } else {
                // 电脑设备保持隐藏状态
                document.getElementById('fileUploadContainer').classList.remove('visible');
            }

            // 初始化功能按钮状态
            initControlButtons();

            // 设置功能按钮点击事件
            document.querySelectorAll('.control-btn').forEach(btn => {
                btn.addEventListener('click', function () {
                    const keyCode = parseInt(this.dataset.key);
                    handleControlButtonClick(keyCode);
                });
            });

            // 确保初始状态没有这些类名
            document.body.classList.remove('static-mode', 'filter-mode');
        };

        // 初始化功能按钮状态
        function initControlButtons() {
            // 设置初始状态
            controlButtons = {
                192: document.body.classList.contains('dark-theme'), // 主题切换
                84: timeColumnsVisible,  // 时间列
                89: statsColumnsVisible, // 统计列
                73: picksColumnsVisible, // 拾取物列
                80: isStaticMode,        // 导出模式
                74: hideZeroStatsTasks,  // 异常任务
                72: isFilterMode         // 任務篩選
            };

            // 更新按钮显示状态
            updateControlButtons();
        }

        // 处理功能按钮点击
        function handleControlButtonClick(keyCode) {
            // 模拟键盘事件
            const event = new KeyboardEvent('keydown', {
                keyCode: keyCode,
                which: keyCode
            });

            // 触发对应的快捷键功能
            document.dispatchEvent(event);

            // 更新按钮状态
            updateButtonState(keyCode);
        }

        // 更新单个按钮状态
        function updateButtonState(keyCode) {
            let isActive = false;

            switch (keyCode) {
                case 192: // 切换颜色
                    isActive = document.body.classList.contains('dark-theme');
                    break;
                case 84: // 时间列
                    isActive = timeColumnsVisible;
                    break;
                case 89: // 统计列
                    isActive = statsColumnsVisible;
                    break;
                case 73: // 拾取物列
                    isActive = picksColumnsVisible;
                    break;
                case 80: // 导出模式
                    isActive = isStaticMode;
                    break;
                case 74: // 异常任务
                    isActive = hideZeroStatsTasks;
                    break;
                case 72: // 任務篩選
                    isActive = isFilterMode;
                    break;
                case 85: // 表格格式切换
                    isActive = useLegacyFormat;
                    break;
            }

            // 更新按钮状态
            controlButtons[keyCode] = isActive;
            const button = document.querySelector(`.control-btn[data-key="${keyCode}"]`);

            if (button) {
                if (isActive) {
                    button.classList.add('active');
                } else {
                    button.classList.remove('active');
                }
            }
        }

        // 更新所有功能按钮状态
        function updateControlButtons() {
            for (const keyCode in controlButtons) {
                updateButtonState(parseInt(keyCode));
            }
        }

        // 处理文件选择事件
        async function handleFileSelect(e) {
            const files = Array.from(e.target.files)
                .filter(f => f.name.startsWith('better-genshin-impact'))
                .sort((a, b) => parseDateFromFileName(a.name) - parseDateFromFileName(b.name));

            if (files.length === 0) {
                alert('请选择BetterGI日志文件（文件名以better-genshin-impact开头）');
                return;
            }

            parsingContext.activeGroups.clear();
            parsingContext.activeTasks.clear();
            parsingContext.allGroups = [];

            try {
                resetTimeTracker();
                document.getElementById('dropZone').innerHTML = '<div class="loading">解析中...</div>';
                document.getElementById('dropZone').className = 'has-content';

                for (const file of files) {
                    const content = await readFileAsText(file);
                    parseLog(content, parseDateFromFileName(file.name));
                }

                const result = finalizeParsing();
                document.getElementById('dropZone').innerHTML = generateHTML(result);
                setupCoordPopups();
                document.querySelectorAll('.group-header').forEach((el, i) => {
                    el.onclick = (e) => {
                        const arrow = el.querySelector('.arrow');
                        const content = document.getElementById(`group-${i}`);
                        const container = el.closest('.group-container');

                        // 获取配置组标题和内容的位置信息
                        const headerRect = el.getBoundingClientRect();
                        const containerRect = container.getBoundingClientRect();

                        // 判断标题是否在可视区域顶部（考虑固定定位）
                        const isStickyAtTop = headerRect.top <= 15 && headerRect.top >= -10;

                        if (isStickyAtTop) {
                            // 计算需要滚动到的位置
                            // 让配置组标题滚动到距离顶部30px的位置
                            const targetPosition = container.offsetTop - 40;
                            const currentPosition = dropZone.scrollTop;
                            const scrollDistance = Math.abs(targetPosition - currentPosition);

                            // 计算滚动时间：根据距离，但不小于300ms，不大于2000ms，然后加上500ms的额外时间
                            const scrollDuration = Math.min(2000, Math.max(200, scrollDistance)) + 50;

                            // 使用平滑滚动
                            dropZone.scrollTo({
                                top: targetPosition,
                                behavior: 'smooth'
                            });

                            // 在滚动完成后再折叠，使用计算出的时间
                            setTimeout(() => {
                                toggleCollapseState(el, content, arrow);
                            }, scrollDuration);

                        } else {
                            // 已经在顶部，直接折叠
                            toggleCollapseState(el, content, arrow);
                        }
                    };
                });

                // 应用初始显示状态
                updateTimeColumnsVisibility();
                updateStatsColumnsVisibility();
                updateTableFormat();
                setupRowHighlight();

                // 使用防抖版本
                setTimeout(() => {
                    debouncedAdjustTableHeader();
                }, 150);

                // 标记已有日志数据
                hasLogData = true;
            } catch (e) {
                document.getElementById('dropZone').innerHTML = `<div class="error">解析失败：${e.message}</div>`;
            }
        }

        // 读取文件内容为文本
        function readFileAsText(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                reader.onload = e => resolve(e.target.result);
                reader.onerror = e => reject(e);
                reader.readAsText(file);
            });
        }

        // 任務篩選功能
        function toggleTaskFilter() {
            if (isFilterMode) {
                // 退出篩選模式
                exitFilterMode();
            } else {
                // 進入篩選模式
                enterFilterMode();
            }

        }

        // 進入篩選模式
        function enterFilterMode() {
            const input = prompt("请输入要筛选的任务关键字（多个关键字用逗号分隔）\n\n例如：蒙德,璃月,稻妻");

            if (input === null) {
                // 用戶取消輸入
                return;
            }

            const keywords = input.split(',')
                .map(keyword => keyword.trim())
                .filter(keyword => keyword.length > 0);

            if (keywords.length === 0) {
                alert("请输入有效的关键字！");
                return;
            }

            currentFilterKeywords = keywords;
            isFilterMode = true;

            // 添加filter-mode类到body
            document.body.classList.add('filter-mode');

            // 應用篩選
            applyTaskFilter();

            // 顯示篩選提示
            showFilterIndicator();

            // 更新按鈕狀態
            updateButtonState(72);
        }

        // 修改退出筛选模式的函数，确保正确处理零统计状态
        function exitFilterMode() {
            isFilterMode = false;
            currentFilterKeywords = [];

            // 移除筛选，显示所有任务和总计行
            removeTaskFilter();

            // 移除filter-mode类
            document.body.classList.remove('filter-mode');

            // 重新应用零统计任务隐藏（如果启用）
            if (hideZeroStatsTasks) {
                toggleZeroStatsTasksNormal(); // 使用非筛选模式版本
            }

            // 隐藏筛选提示
            hideFilterIndicator();

            // 更新按钮状态
            updateButtonState(72);
        }

        // 修改 applyTaskFilter 函数，在筛选模式下隐藏所有拾取物
        function applyTaskFilter() {
            const tables = document.querySelectorAll('table');

            tables.forEach(table => {
                const container = table.closest('.group-container');
                const groupHeader = container.querySelector('.group-header h2');
                const groupName = groupHeader ? groupHeader.textContent : '';

                let groupHasVisibleTask = false;

                // 确保队伍角色行显示
                const teamRolesRow = table.querySelector('.team-roles-row');
                if (teamRolesRow) {
                    teamRolesRow.style.display = '';
                }

                // 隐藏所有拾取物行
                const summaryRows = table.querySelectorAll('.summary-row');
                summaryRows.forEach(row => {
                    row.style.display = 'none';
                });




                // 第一步：检查配置组名是否匹配关键字
                const groupMatchesKeyword = currentFilterKeywords.some(keyword =>
                    groupName.toLowerCase().includes(keyword.toLowerCase())
                );

                // 如果配置组名匹配，显示整个配置组的所有任务
                if (groupMatchesKeyword) {
                    // 显示所有任务行
                    const rows = table.querySelectorAll('tr');
                    rows.forEach(row => {
                        if (!row.querySelector('th') && !row.classList.contains('team-roles-row') && !row.classList.contains('summary-row')) {
                            row.style.display = '';
                        }
                    });
                    groupHasVisibleTask = true;
                }
                // 如果配置组名不匹配，检查组内任务
                else {
                    // 新版格式
                    if (!useLegacyFormat) {
                        const rows = table.querySelectorAll('tr');

                        rows.forEach(row => {
                            // 跳过表头行、队伍角色行和已经隐藏的summary-row
                            if (row.querySelector('th') || row.classList.contains('team-roles-row') || row.classList.contains('summary-row')) {
                                return;
                            }

                            // 获取任务名称
                            const taskNameCell = row.querySelector('td:first-child');
                            if (taskNameCell) {
                                const taskName = taskNameCell.textContent.toLowerCase();

                                // 检查任务名称是否包含任一关键字
                                const matchesKeyword = currentFilterKeywords.some(keyword =>
                                    taskName.includes(keyword.toLowerCase())
                                );

                                // 检查统计列是否非零
                                let matchesStats = true;
                                if (hideZeroStatsTasks) {
                                    const reviveCell = row.querySelector('.column-revive');
                                    const retryCell = row.querySelector('.column-retry');
                                    const retryDetailCell = row.querySelector('.column-retry-detail');
                                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                    const timeoutCell = row.querySelector('.column-timeout');
                                    const teleportCell = row.querySelector('.column-teleport');

                                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                        const isAllZero =
                                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                        matchesStats = !isAllZero;
                                    }
                                }

                                if (matchesKeyword && matchesStats) {
                                    row.style.display = '';
                                    groupHasVisibleTask = true;
                                } else {
                                    row.style.display = 'none';
                                }
                            }
                        });
                    }
                    // 旧版格式
                    else {
                        const taskNameRows = table.querySelectorAll('tr .main-row-name');

                        taskNameRows.forEach(taskNameCell => {
                            const row = taskNameCell.closest('tr');
                            const taskId = row.getAttribute('data-task-id');

                            if (taskId) {
                                const taskName = taskNameCell.textContent.toLowerCase();

                                // 检查任务名称是否包含任一关键字
                                const matchesKeyword = currentFilterKeywords.some(keyword =>
                                    taskName.includes(keyword.toLowerCase())
                                );

                                // 检查统计列是否非零
                                let matchesStats = true;
                                if (hideZeroStatsTasks) {
                                    const reviveCell = row.querySelector('.column-revive');
                                    const retryCell = row.querySelector('.column-retry');
                                    const retryDetailCell = row.querySelector('.column-retry-detail');
                                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                    const timeoutCell = row.querySelector('.column-timeout');
                                    const teleportCell = row.querySelector('.column-teleport');

                                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                        const isAllZero =
                                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                        matchesStats = !isAllZero;
                                    }
                                }

                                if (matchesKeyword && matchesStats) {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        if (!r.classList.contains('summary-row')) {
                                            r.style.display = '';
                                        }
                                    });
                                    groupHasVisibleTask = true;
                                } else {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        r.style.display = 'none';
                                    });
                                }
                            }
                        });
                    }
                }



                // 如果配置组没有可见任务，隐藏整个配置组（包括队伍角色行）
                if (groupHasVisibleTask) {
                    container.style.display = '';
                } else {
                    container.style.display = 'none';
                }
            });

            // 筛选后调整表头位置
            setTimeout(() => {
                adjustTableHeaderPosition();
            }, 50);
        }

        function removeTaskFilter() {
            const tables = document.querySelectorAll('table');

            tables.forEach(table => {
                const container = table.closest('.group-container');

                // 确保队伍角色行显示
                const teamRolesRow = table.querySelector('.team-roles-row');
                if (teamRolesRow) {
                    teamRolesRow.style.display = '';
                }

                // 显示所有行，包括拾取物行
                const rows = table.querySelectorAll('tr');
                rows.forEach(row => {
                    if (!row.querySelector('th')) {
                        if (row.classList.contains('summary-row')) {
                            row.style.display = hideZeroStatsTasks ? 'none' : '';
                        } else {
                            row.style.display = '';
                        }
                    }
                });

                // 显示配置组
                container.style.display = '';
            });

            // 重新调整表头位置
            setTimeout(() => {
                adjustTableHeaderPosition();
            }, 50);
        }

        // 顯示篩選提示
        function showFilterIndicator() {
            const indicator = document.getElementById('filterIndicator');
            const keywordsElement = document.getElementById('filterKeywords');

            keywordsElement.textContent = currentFilterKeywords.join(', ');
            indicator.classList.add('visible');

            // 动态计算筛选提示宽度并设置CSS变量
            const filterWidth = indicator.offsetWidth;
            document.documentElement.style.setProperty('--filter-indicator-width', filterWidth + 'px');
        }

        // 隱藏篩選提示
        function hideFilterIndicator() {
            const indicator = document.getElementById('filterIndicator');
            indicator.classList.remove('visible');

            // 移除CSS变量
            document.documentElement.style.removeProperty('--filter-indicator-width');
        }

        // 拖放事件处理
        const dropZone = document.getElementById('dropZone');

        dropZone.addEventListener('dragover', (e) => {
            e.preventDefault();
            dropZone.classList.add('dragover');
        });

        dropZone.addEventListener('dragleave', () => {
            dropZone.classList.remove('dragover');
        });

        dropZone.addEventListener('drop', async e => {
            e.preventDefault();
            dropZone.style.backgroundColor = '';

            const files = Array.from(e.dataTransfer.files)
                .filter(f => f.name.startsWith('better-genshin-impact'))
                .sort((a, b) => parseDateFromFileName(a.name) - parseDateFromFileName(b.name));

            if (files.length === 0) return;

            parsingContext.activeGroups.clear();
            parsingContext.activeTasks.clear();
            parsingContext.allGroups = [];

            try {
                resetTimeTracker();
                dropZone.innerHTML = '<div class="loading">解析中...</div>';

                for (const file of files) {
                    const content = await file.text();
                    parseLog(content, parseDateFromFileName(file.name));
                }

                const result = finalizeParsing();
                dropZone.className = 'has-content';
                dropZone.innerHTML = generateHTML(result);
                setupCoordPopups();
                // 修改折叠功能事件处理
                document.querySelectorAll('.group-header').forEach((el, i) => {
                    el.onclick = (e) => {
                        const arrow = el.querySelector('.arrow');
                        const content = document.getElementById(`group-${i}`);
                        const container = el.closest('.group-container');

                        // 获取配置组标题和内容的位置信息
                        const headerRect = el.getBoundingClientRect();
                        const containerRect = container.getBoundingClientRect();

                        // 判断标题是否在可视区域顶部（考虑固定定位）
                        const isStickyAtTop = headerRect.top <= 15 && headerRect.top >= -10;

                        if (isStickyAtTop) {
                            // 计算需要滚动到的位置
                            // 让配置组标题滚动到距离顶部30px的位置
                            const targetPosition = container.offsetTop - 40;
                            const currentPosition = dropZone.scrollTop;
                            const scrollDistance = Math.abs(targetPosition - currentPosition);

                            // 计算滚动时间：根据距离，但不小于300ms，不大于2000ms，然后加上500ms的额外时间
                            const scrollDuration = Math.min(2000, Math.max(200, scrollDistance)) + 50;

                            // 使用平滑滚动
                            dropZone.scrollTo({
                                top: targetPosition,
                                behavior: 'smooth'
                            });

                            // 在滚动完成后再折叠，使用计算出的时间
                            setTimeout(() => {
                                toggleCollapseState(el, content, arrow);
                            }, scrollDuration);

                        } else {
                            // 已经在顶部，直接折叠
                            toggleCollapseState(el, content, arrow);
                        }
                    };
                });

                // 应用初始时间列显示状态
                updateTimeColumnsVisibility();
                // 应用初始统计列显示状态
                updateStatsColumnsVisibility();
                // 应用初始表格格式
                updateTableFormat();

                // 设置行高亮效果
                setupRowHighlight();

                // 第二次讀檔時保留各功能状态
                if (hasLogData = true) {
                    refreshHTML();
                }
                // 标记已有日志数据
                hasLogData = true;
            } catch (e) {
                dropZone.innerHTML = `<div class="error">解析失败：${e.message}</div>`;
            }
        });

        // 修改坐标弹窗事件处理函数
        function setupCoordPopups() {
            const coordsCells = document.querySelectorAll('.coords-cell');
            const battleCells = document.querySelectorAll('.has-battle-events');
            let popupPositions = new WeakMap(); // 存储每个弹窗的位置状态
            let pinnedPopup = null;
            let isDragging = false;
            let dragOffsetX, dragOffsetY;
            let dragElement = null;
            const allPopups = []; // 存储所有弹窗元素

            // 添加双击重置功能
            let lastClickTime = 0;
            let clickTimeout = null;

            // 定位弹窗函数
            const positionPopup = (popup, cell) => {
                // 如果已有位置记录，使用记录的位置
                if (popupPositions.has(popup)) {
                    const pos = popupPositions.get(popup);
                    popup.style.left = pos.x + 'px';
                    popup.style.top = pos.y + 'px';
                    return;
                }

                // 否则定位在单元格中间
                const cellRect = cell.getBoundingClientRect();
                const popupRect = popup.getBoundingClientRect();

                // 计算位置 - 单元格中间
                let left = cellRect.left + (cellRect.width - popupRect.width) / 2;
                let top = cellRect.top + (cellRect.height - popupRect.height) / 2;

                // 确保不会超出视口
                if (left < 5) left = 5;
                if (top < 5) top = 5;
                if (left + popupRect.width > window.innerWidth) {
                    left = window.innerWidth - popupRect.width - 5;
                }
                if (top + popupRect.height > window.innerHeight) {
                    top = window.innerHeight - popupRect.height - 5;
                }

                popup.style.left = left + 'px';
                popup.style.top = top + 'px';

                // 存储初始位置
                popupPositions.set(popup, { x: left, y: top });
            };

            // 处理坐标弹窗单元格
            coordsCells.forEach(cell => {
                const popup = cell.querySelector('.coord-popup');
                if (!popup) return;

                // 双击事件处理
                cell.addEventListener('dblclick', (e) => {
                    e.stopPropagation();

                    // 重置弹窗位置
                    if (popup) {
                        // 清除位置记录，强制重新定位到中间
                        popupPositions.delete(popup);
                        positionPopup(popup, cell);

                        // 确保弹窗可见
                        popup.classList.add('visible');
                    }
                });

                // 只在有坐标数据时才添加点击事件
                if (cell.classList.contains('has-coords')) {
                    // 点击单元格固定/取消固定弹窗
                    cell.addEventListener('click', (e) => {
                        // 阻止事件冒泡，避免触发弹窗的点击事件
                        e.stopPropagation();

                        // 忽略标题栏和关闭按钮的点击
                        if (e.target.closest('.coord-popup-header')) return;

                        if (popup.classList.contains('pinned')) {
                            // 取消固定
                            popup.classList.remove('pinned');
                            cell.classList.remove('pinned');
                            pinnedPopup = null;

                            // 如果鼠标不在单元格上，隐藏弹窗
                            if (!cell.matches(':hover')) {
                                popup.classList.remove('visible');
                            }
                        } else {
                            // 固定弹窗
                            pinPopup(popup, cell);
                        }
                    });
                }

                // 固定弹窗函数
                const pinPopup = (popup, cell) => {
                    popup.classList.add('pinned');
                    cell.classList.add('pinned');
                    pinnedPopup = popup;

                    // 确保弹窗可见
                    popup.classList.add('visible');

                    // 存储当前位置
                    popupPositions.set(popup, {
                        x: parseInt(popup.style.left),
                        y: parseInt(popup.style.top)
                    });
                };

                // 鼠标悬停显示弹窗（只在有坐标数据时）
                if (cell.classList.contains('has-coords')) {
                    cell.addEventListener('mouseenter', (e) => {
                        // 如果已有其他弹窗固定，不做处理
                        if (pinnedPopup && pinnedPopup !== popup) return;

                        positionPopup(popup, cell);
                        popup.classList.add('visible');
                    });

                    cell.addEventListener('mouseleave', (e) => {
                        // 检查鼠标是否移动到弹窗上
                        if (popup.contains(e.relatedTarget)) return;

                        // 如果弹窗未固定，隐藏它
                        if (!popup.classList.contains('pinned')) {
                            popup.classList.remove('visible');
                        }
                    });
                }

                // 弹窗鼠标进入/离开处理
                popup.addEventListener('mouseenter', () => {
                    // 防止单元格mouseleave事件触发
                    popup._hovering = true;
                });

                popup.addEventListener('mouseleave', () => {
                    popup._hovering = false;

                    // 如果弹窗未固定且鼠标不在单元格上，隐藏弹窗
                    if (!popup.classList.contains('pinned') && !cell.matches(':hover')) {
                        popup.classList.remove('visible');
                    }
                });

                // 在创建弹窗后添加到 allPopups
                if (popup) {
                    allPopups.push(popup);
                }

                // 点击关闭按钮
                const closeBtn = popup.querySelector('.close-btn');
                closeBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    popup.classList.remove('visible', 'pinned');
                    cell.classList.remove('pinned');
                    pinnedPopup = null;

                    // 清除位置记录
                    popupPositions.delete(popup);
                });

                // 标题栏拖拽功能 - 移动时自动固定
                const header = popup.querySelector('.coord-popup-header');
                header.addEventListener('mousedown', (e) => {
                    if (e.target === closeBtn) return;

                    isDragging = true;
                    dragElement = popup;
                    const rect = popup.getBoundingClientRect();
                    dragOffsetX = e.clientX - rect.left;
                    dragOffsetY = e.clientY - rect.top;

                    // 移动时自动固定弹窗
                    if (!popup.classList.contains('pinned')) {
                        pinPopup(popup, cell);
                    }

                    e.preventDefault();
                });

                // 阻止内容区域的点击事件冒泡
                popup.addEventListener('click', (e) => {
                    e.stopPropagation();
                });

            });

            // 处理战斗事件弹窗单元格
            battleCells.forEach(cell => {
                const popups = cell.querySelectorAll('.battle-popup');
                if (!popups || popups.length === 0) return;

                popups.forEach(popup => {
                    // 双击事件处理
                    cell.addEventListener('dblclick', (e) => {
                        e.stopPropagation();

                        // 重置弹窗位置
                        if (popup) {
                            // 清除位置记录，强制重新定位到中间
                            popupPositions.delete(popup);
                            positionPopup(popup, cell);

                            // 确保弹窗可见
                            popup.classList.add('visible');
                        }
                    });

                    // 点击单元格固定/取消固定弹窗
                    cell.addEventListener('click', (e) => {
                        e.stopPropagation();
                        if (e.target.closest('.battle-popup-header')) return;

                        if (popup.classList.contains('pinned')) {
                            popup.classList.remove('pinned');
                            cell.classList.remove('pinned');
                            pinnedPopup = null;
                            if (!cell.matches(':hover')) {
                                popup.classList.remove('visible');
                            }
                        } else {
                            pinPopup(popup, cell);
                        }
                    });

                    // 固定弹窗函数
                    const pinPopup = (popup, cell) => {
                        popup.classList.add('pinned');
                        cell.classList.add('pinned');
                        pinnedPopup = popup;
                        popup.classList.add('visible');
                        positionPopup(popup, cell);
                        popupPositions.set(popup, {
                            x: parseInt(popup.style.left),
                            y: parseInt(popup.style.top)
                        });
                    };

                    // 鼠标悬停显示弹窗
                    cell.addEventListener('mouseenter', (e) => {
                        if (pinnedPopup && pinnedPopup !== popup) return;
                        positionPopup(popup, cell);
                        popup.classList.add('visible');
                    });

                    cell.addEventListener('mouseleave', (e) => {
                        if (popup.contains(e.relatedTarget)) return;
                        if (!popup.classList.contains('pinned')) {
                            popup.classList.remove('visible');
                        }
                    });

                    // 弹窗鼠标进入/离开处理
                    popup.addEventListener('mouseenter', () => {
                        popup._hovering = true;
                    });

                    popup.addEventListener('mouseleave', () => {
                        popup._hovering = false;
                        if (!popup.classList.contains('pinned') && !cell.matches(':hover')) {
                            popup.classList.remove('visible');
                        }
                    });

                    // 添加到全局弹窗列表
                    allPopups.push(popup);

                    // 点击关闭按钮
                    const closeBtn = popup.querySelector('.close-btn');
                    closeBtn.addEventListener('click', (e) => {
                        e.stopPropagation();
                        popup.classList.remove('visible', 'pinned');
                        cell.classList.remove('pinned');
                        pinnedPopup = null;
                        popupPositions.delete(popup);
                    });

                    // 标题栏拖拽功能
                    const header = popup.querySelector('.battle-popup-header');
                    header.addEventListener('mousedown', (e) => {
                        if (e.target === closeBtn) return;
                        isDragging = true;
                        dragElement = popup;
                        const rect = popup.getBoundingClientRect();
                        dragOffsetX = e.clientX - rect.left;
                        dragOffsetY = e.clientY - rect.top;
                        if (!popup.classList.contains('pinned')) {
                            pinPopup(popup, cell);
                        }
                        e.preventDefault();
                    });

                    // 阻止内容区域的点击事件冒泡
                    popup.addEventListener('click', (e) => {
                        e.stopPropagation();
                    });
                });
            });

            // 全局鼠标事件处理拖拽
            document.addEventListener('mousemove', (e) => {
                if (!isDragging || !dragElement) return;

                dragElement.style.left = (e.clientX - dragOffsetX) + 'px';
                dragElement.style.top = (e.clientY - dragOffsetY) + 'px';

                // 更新位置记录
                popupPositions.set(dragElement, {
                    x: parseInt(dragElement.style.left),
                    y: parseInt(dragElement.style.top)
                });
            });

            document.addEventListener('mouseup', () => {
                if (isDragging && dragElement) {
                    // 更新位置记录
                    popupPositions.set(dragElement, {
                        x: parseInt(dragElement.style.left),
                        y: parseInt(dragElement.style.top)
                    });
                }
                isDragging = false;
                dragElement = null;
            });

            // 添加重置函数
            function resetCoordPopups() {
                // 关闭所有可见弹窗
                allPopups.forEach(popup => {
                    popup.classList.remove('visible', 'pinned');

                    // 移除单元格的固定状态
                    const cell = popup.closest('.coords-cell, .has-battle-events');
                    if (cell) {
                        cell.classList.remove('pinned');
                    }
                });

                // 清除位置记录
                popupPositions = new WeakMap();
                pinnedPopup = null;

                // 重新定位所有弹窗到单元格中间
                coordsCells.forEach(cell => {
                    const popup = cell.querySelector('.coord-popup');
                    if (popup) { positionPopup(popup, cell); }
                });
                battleCells.forEach(cell => {
                    const popups = cell.querySelectorAll('.battle-popup');
                    popups.forEach(popup => positionPopup(popup, cell));
                });
            }

            // 暴露重置函数到全局
            window.resetCoordPopups = resetCoordPopups;

            // 窗口大小变化时重新定位
            window.addEventListener('resize', () => {
                document.querySelectorAll('.coord-popup.visible, .battle-popup.visible').forEach(popup => {
                    // 如果弹窗有位置记录，调整位置确保在视口内
                    if (popupPositions.has(popup)) {
                        const pos = popupPositions.get(popup);
                        const popupRect = popup.getBoundingClientRect();

                        // 检查位置是否在视口内
                        let newLeft = pos.x;
                        let newTop = pos.y;

                        if (newLeft + popupRect.width > window.innerWidth) {
                            newLeft = window.innerWidth - popupRect.width - 5;
                        }
                        if (newTop + popupRect.height > window.innerHeight) {
                            newTop = window.innerHeight - popupRect.height - 5;
                        }
                        if (newLeft < 5) newLeft = 5;
                        if (newTop < 5) newTop = 5;

                        // 更新位置
                        popup.style.left = newLeft + 'px';
                        popup.style.top = newTop + 'px';

                        // 更新位置记录
                        popupPositions.set(popup, { x: newLeft, y: newTop });
                    } else {
                        // 如果没有位置记录，重新定位到单元格中间
                        const cell = popup.closest('.coords-cell, .has-battle-events');
                        if (cell) {
                            positionPopup(popup, cell);
                        }
                    }
                });
            });

            // 窗口大小变化时使用防抖
            window.addEventListener('resize', debouncedAdjustTableHeader);
        }

        // 文件处理流程
        async function processFiles(files) {
            return files
                .map(file => ({
                    file, // 保留原始File对象
                    date: parseDateFromFileName(file.name)
                }))
                .sort((a, b) => a.date - b.date) // 按日期从新到新排序
                .map(entry => ({
                    file: entry.file, // 直接传递File对象
                    date: entry.date
                }));
        }

        // 从文件名解析日期
        function parseDateFromFileName(fileName) {
            const match = fileName.match(/(\d{4})(\d{2})(\d{2})/);
            if (match) {
                return new Date(
                    parseInt(match[1]),
                    parseInt(match[2]) - 1,
                    parseInt(match[3])
                );
            }
            return new Date(0); // 无效日期置前
        }

        // 日志解析逻辑
        function parseLog(content, fileDate) {
            const lines = content.split('\n');
            let prevLine = "";
            let lastCoordForRevive = null; // 用于血量恢复完成的坐标跟踪
            let lastCoordForStuck = null;  // 用于疑似卡死的坐标跟踪
            let battleCount = 0; // 自动战斗计数器

            // 精细加权系统
            const WEIGHTS = {
                SURVIVAL_AFTER_SKILL: 20,       // 生存位技能后的切换 +20
                NORMAL_SWITCH: 1,               // 一般成功切换角色 +1
                COLLECTION_SWITCH: -100,        // 采集相关切换 -100（确保排除）
                MOVEMENT_CONTEXT: 5              // 新增：移动上下文权重 +8
            };

            // 任务级别的角色统计
            let survivalWeights = new Map(); //  生存位权重
            let walkWeights = new Map(); // 行走位权重
            let lastSwitchedRole = null; // 记录最近切换的角色
            let pendingSurvivalAction = false; // 标记有生存位动作待处理
            let nahidaCollect = false; // 纳西妲采集标记
            let kazuhaCollect = false; // 枫原万叶采集标记

            let currentGroup = null;
            let currentTask = null;

            lines.forEach(line => {
                // 在每行开始时更新当前组和任务
                currentGroup = Array.from(parsingContext.activeGroups.values()).pop() || null;
                if (currentGroup) {
                    currentTask = currentGroup.tasks.length > 0 ? currentGroup.tasks[currentGroup.tasks.length - 1] : null;
                } else {
                    currentTask = null;
                }

                // 检测采集动作
                if (line.includes('执行 "纳西妲" 长按E转圈拾取')) {
                    nahidaCollect = true;
                } else if (line.includes('使用枫原万叶长E拾取掉落物')) {
                    kazuhaCollect = true;
                }

                // 检测生存位技能指令
                if (line.includes('切换盾、回血角色，使用元素战技')) {
                    pendingSurvivalAction = true;
                }


                // 检测成功切换角色
                if (line.includes('成功切换角色:"')) {
                    const roleMatch = line.match(/成功切换角色:"(.+?)"/);
                    if (roleMatch && currentGroup && currentTask) {
                        const roleName = roleMatch[1];
                        lastSwitchedRole = roleName; // 更新最近切换的角色

                        let survivalWeightToAdd = 0;
                        let walkWeightToAdd = 0;

                        // 采集相关的切换 - 给负权重确保排除
                        if (nahidaCollect || kazuhaCollect) {
                        }
                        // 生存位技能后的切换 - 高权重
                        else if (pendingSurvivalAction) {
                            survivalWeightToAdd = WEIGHTS.SURVIVAL_AFTER_SKILL;
                            walkWeightToAdd = WEIGHTS.NORMAL_SWITCH; // 同时也计入行走位
                            pendingSurvivalAction = false;
                        }
                        // 一般切换 - 正常权重
                        else {
                            walkWeightToAdd = WEIGHTS.NORMAL_SWITCH * 10;
                        }

                        // 新增：移动上下文权重判断
                        if (prevLine.includes('到达路径点附近') ||
                            prevLine.includes('执行 "简易策略脚本"') ||
                            prevLine.includes('粗略接近途经点') ||
                            prevLine.includes('精确接近目标点')) {
                            walkWeightToAdd += WEIGHTS.MOVEMENT_CONTEXT;
                        }

                        // 新增：交互或拾取后的切换权重 +8
                        if (prevLine.includes('交互或拾取："')) {
                            walkWeightToAdd += 8;
                        }

                        // 更新权重
                        if (survivalWeightToAdd !== 0) {
                            const currentSurvivalWeight = survivalWeights.get(roleName) || 0;
                            survivalWeights.set(roleName, currentSurvivalWeight + survivalWeightToAdd);
                        }

                        if (walkWeightToAdd !== 0) {
                            const currentWalkWeight = walkWeights.get(roleName) || 0;
                            walkWeights.set(roleName, currentWalkWeight + walkWeightToAdd);
                        }

                        // 重置采集标记
                        nahidaCollect = false;
                        kazuhaCollect = false;
                    }
                }

                // 解析配置组
                let match = line.match(/配置组 "(.+?)" 加载完成/);
                if (match) {
                    const groupName = match[1];
                    let group = parsingContext.activeGroups.get(groupName) || {
                        name: groupName,
                        startTime: parseTime(prevLine, fileDate),
                        endTime: null,
                        tasks: [],
                        picks: new Map(),
                        lastActive: null,
                        teamRoles: new Set()  // 新增：存储不同的队伍角色组合
                    };
                    group.lastActive = parseTime(prevLine, fileDate);
                    parsingContext.activeGroups.set(groupName, group);
                    battleCount = 0; // 重置战斗计数器
                    lastCoordForRevive = null; // 重置坐标跟踪
                    lastCoordForStuck = null;  // 重置坐标跟踪

                    // 重置角色统计
                    survivalWeights.clear();
                    walkWeights.clear();
                    lastSwitchedRole = null;
                    pendingSurvivalAction = false;
                    nahidaCollect = false;
                    kazuhaCollect = false;

                    // 更新当前组
                    currentGroup = group;
                }

                // 配置组结束
                match = line.match(/配置组 "(.+?)" 执行结束/);
                if (match) {
                    const group = parsingContext.activeGroups.get(match[1]);
                    if (group) {
                        group.endTime = parseTime(prevLine, fileDate);

                        // 汇总配置组的角色信息
                        const groupSurvivalRoles = new Map();
                        const groupWalkRoles = new Map();

                        group.tasks.forEach(task => {
                            // 汇总生存位 - 只取每个任务的主要生存位
                            if (task.survivalRoles && task.survivalRoles.length > 0) {
                                const mainSurvivalRole = task.survivalRoles[0];
                                const count = groupSurvivalRoles.get(mainSurvivalRole) || 0;
                                groupSurvivalRoles.set(mainSurvivalRole, count + 1);
                            }

                            // 汇总行走位 - 只取每个任务的主要行走位
                            if (task.walkRoles && task.walkRoles.length > 0) {
                                const mainWalkRole = task.walkRoles[0];
                                const count = groupWalkRoles.get(mainWalkRole) || 0;
                                groupWalkRoles.set(mainWalkRole, count + 1);
                            }
                        });

                        // 确定最终的配置组角色
                        // 生存位：取出现次数最多的一个
                        const sortedSurvival = Array.from(groupSurvivalRoles.entries())
                            .sort((a, b) => b[1] - a[1]);
                        group.survivalRoles = sortedSurvival.length > 0 ? [sortedSurvival[0][0]] : [];

                        // 行走位：取出现次数最多的一个
                        const sortedWalk = Array.from(groupWalkRoles.entries())
                            .sort((a, b) => b[1] - a[1]);
                        group.walkRoles = sortedWalk.length > 0 ? [sortedWalk[0][0]] : [];

                        parsingContext.allGroups.push(group);
                        parsingContext.activeGroups.delete(match[1]);

                        // 重置全局角色计数
                        survivalWeights.clear();
                        walkWeights.clear();
                        lastSwitchedRole = null;
                        pendingSurvivalAction = false;
                        nahidaCollect = false;
                        kazuhaCollect = false;

                        currentGroup = Array.from(parsingContext.activeGroups.values()).pop() || null;
                    }
                }

                // 解析任务
                if (currentGroup) {
                    // 识别到的队伍角色
                    if (line.includes('识别到的队伍角色:"')) {
                        const match = line.match(/识别到的队伍角色:"(.+?)"/);
                        if (match && currentGroup) {
                            const teamRoleStr = match[1];
                            currentGroup.teamRoles.add(teamRoleStr);
                        }
                    }

                    // 检测自动战斗开始
                    if (line.includes('执行 "自动战斗"')) {
                        battleCount++;
                    }

                    // match = line.match(/→ 开始执行地图追踪任务: "(.+?)"/);
                    // match = line.match(/→ 开始执行路径追踪任务: "(.+?)"/)
                    // match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"/);
                    // match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"|assets\/(.+?\.json)/);
                    // match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"|assets\/(.+?\.json)|当前钓鱼点: (.+)/);
                    // match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"|assets\/(.+?\.json)|当前钓鱼点: (.+)|→ 开始执行JS脚本: "(.+?)"/);
                    // match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"|开始购买NPC: "(.+?)"|当前钓鱼点: (.+)|→ 开始执行JS脚本: "(.+?)"/);
                    match = line.match(/→ 开始执行(?:地图|路径)追踪任务: "(.+?)"|开始购买NPC: "(.+?)"|assets\/(.+?\.json)|当前钓鱼点: (.+)|→ 开始执行JS脚本: "(.+?)"/);
                    if (match) {
                        // const taskName = match[1] || match[2] || match[3] || match[4];
                        const taskName = match[1] || match[2] || match[3] || match[4] || match[5];
                        const task = {
                            name: taskName,
                            startTime: parseTime(prevLine, fileDate),
                            endTime: null,
                            faults: {
                                revive: 0,
                                teleport: 0,
                                retry: 0,
                                retryDetail: 0,
                                too_far_skip: 0,
                                timeout: 0
                            },
                            picks: new Map(),
                            reviveEvents: [], // 新增：复活事件
                            timeoutEvents: [], // 新增：超时事件
                            maxBattleCount: 0, // 记录最大战斗次数
                            specialAlerts: new Map(), // 修改：使用Map而不是数组，用于计数特殊事件
                            survivalRoles: [], // 新增：生存位角色
                            walkRoles: []      // 新增：行走位角色
                        };
                        currentGroup.tasks.push(task);
                        parsingContext.activeTasks.set(task.name, task);
                        battleCount = 0;

                        // 重置任务级别的角色统计
                        survivalWeights.clear();
                        walkWeights.clear();
                        lastSwitchedRole = null;
                        pendingSurvivalAction = false;
                        nahidaCollect = false;
                        kazuhaCollect = false;

                        // 更新当前任务
                        currentTask = task;
                    }

                    // 任務結束
                    match = line.match(/→ 脚本执行结束: "(.+?)"/);
                    if (match) {
                        const task = parsingContext.activeTasks.get(match[1]);
                        if (task) {
                            task.endTime = parseTime(prevLine, fileDate);

                            // 任务结束时，如果还有待处理的生存位动作，使用最近切换的角色
                            if (pendingSurvivalAction && lastSwitchedRole && !nahidaCollect && !kazuhaCollect) {
                                const currentWeight = survivalWeights.get(lastSwitchedRole) || 0;
                                survivalWeights.set(lastSwitchedRole, currentWeight + 3); // 中等权重
                                pendingSurvivalAction = false;
                            }

                            // 确定生存位：取权重最高的角色（只考虑正权重）
                            let finalSurvivalRole = null;
                            if (survivalWeights.size > 0) {
                                const sortedSurvival = Array.from(survivalWeights.entries())
                                    .filter(([role, weight]) => weight > 0) // 只考虑正权重
                                    .sort((a, b) => b[1] - a[1]);
                                if (sortedSurvival.length > 0) {
                                    finalSurvivalRole = sortedSurvival[0][0];
                                }
                            }

                            // 确定行走位：取权重最高的角色（只考虑正权重）
                            let finalWalkRole = null;
                            if (walkWeights.size > 0) {
                                const sortedWalk = Array.from(walkWeights.entries())
                                    .filter(([role, weight]) => weight > 0) // 只考虑正权重
                                    .sort((a, b) => b[1] - a[1]);
                                if (sortedWalk.length > 0) {
                                    finalWalkRole = sortedWalk[0][0];
                                }
                            }

                            // 保存到任务
                            task.survivalRoles = finalSurvivalRole ? [finalSurvivalRole] : [];
                            task.walkRoles = finalWalkRole ? [finalWalkRole] : [];

                            // 调试输出
                            // console.log(`任务 ${task.name} 生存位权重:`, Array.from(survivalWeights.entries()));
                            // console.log(`任务 ${task.name} 行走位权重:`, Array.from(walkWeights.entries()));
                            // console.log(`任务 ${task.name} 最终结果:`, {
                            //     survival: task.survivalRoles,
                            //     walk: task.walkRoles
                            // });

                            parsingContext.activeTasks.delete(task.name);

                            // 重置角色计数
                            survivalWeights.clear();
                            walkWeights.clear();
                            lastSwitchedRole = null;
                            pendingSurvivalAction = false;
                            nahidaCollect = false;
                            kazuhaCollect = false;

                            // 更新当前任务
                            currentTask = currentGroup.tasks.length > 0 ? currentGroup.tasks[currentGroup.tasks.length - 1] : null;
                        }
                    }

                    // 统计信息
                    if (currentTask) {
                        // 更新最大战斗次数
                        if (battleCount > currentTask.maxBattleCount) {
                            currentTask.maxBattleCount = battleCount;
                        }

                        // 捕获坐标信息（无论是否在卡死前）
                        // const coordMatch = line.match(/粗略接近途经点，位置\("(-?\d+(?:\.\d+)?)","(-?\d+(?:\.\d+)?)"\)/);
                        const coordMatch = line.match(/(?:粗略接近途经点|精确接近目标点)，位置\("(-?\d+(?:\.\d+)?)","(-?\d+(?:\.\d+)?)"\)/);
                        if (coordMatch) {
                            // 同时更新两个坐标跟踪器
                            lastCoordForRevive = { x: coordMatch[1], y: coordMatch[2] };
                            lastCoordForStuck = { x: coordMatch[1], y: coordMatch[2] };
                        }

                        // if (line.includes("前往七天神像复活")) currentTask.faults.revive++;

                        // 复活事件
                        if (line.includes("血量恢复完成。")) {
                            currentTask.faults.revive++;
                            // 记录复活事件对应的战斗序号和坐标
                            currentTask.reviveEvents.push({
                                battleCount: battleCount,
                                coord: lastCoordForRevive ? { x: lastCoordForRevive.x, y: lastCoordForRevive.y } : null
                            });
                            // 重置复活坐标跟踪器
                            lastCoordForRevive = null;
                        }

                        if (line.includes("当前角色血量过低")) currentTask.faults.revive = currentTask.faults.revive - 0.9;
                        if (line.match(/传送失败，重试 (\d+) 次/))
                            currentTask.faults.teleport = parseInt(RegExp.$1);
                        // if (line.includes("重试一次路线")) currentTask.faults.retry++;
                        if (line.includes("记录恢复点位，地图追踪将到达上次点位之前将跳过走路之外的操作")) currentTask.faults.retry++;
                        if (line.includes("疑似卡死，尝试脱离...")) {
                            currentTask.faults.retryDetail++;

                            // 使用最近捕获的坐标
                            if (lastCoordForStuck) {
                                if (!currentTask.faults.coords) {
                                    currentTask.faults.coords = [];
                                }
                                currentTask.faults.coords.push(lastCoordForStuck);
                                // 重置卡死坐标跟踪器
                                lastCoordForStuck = null;
                            }
                        }
                        // if (line.includes("距离过远，跳过路径点")) currentTask.faults.too_far_skip++;
                        if (line.includes("距离过远") && /\(\d+\.?\d*,\d+\.?\d*\)->\(\d+\.?\d*,\d+\.?\d*\)=\d+\.?\d*，跳过路径点/)
                            currentTask.faults.too_far_skip++;

                        // 战斗超时事件
                        if (line.includes("战斗超时")) {
                            currentTask.faults.timeout++;
                            // 记录超时事件对应的战斗序号
                            currentTask.timeoutEvents.push(battleCount);
                        }

                        // 特殊事件的处理逻辑，使用计数而不是重复添加
                        // 0. 识别到的队伍角色:".*"
                        // test. 測試用例子
                        // if (line.includes('识别到的队伍角色:"')) {
                        //     const match = line.match(/识别到的队伍角色:"(.+?)"/);
                        //     if (match) {
                        //         const message = `队伍角色:"${match[1]}"`;
                        //         const type = 'auto-potion';
                        //         if (currentTask.specialAlerts.has(message)) {
                        //             const existing = currentTask.specialAlerts.get(message);
                        //             existing.count++;
                        //         } else {
                        //             currentTask.specialAlerts.set(message, { type: type, count: 1 });
                        //         }
                        //     }
                        // }
                        // if (line.includes('成功切换角色:"')) {
                        //     const match = line.match(/成功切换角色:"(.+?)"/);
                        //     if (match) {
                        //         const message = `成功切换角色:"${match[1]}"`;
                        //         const type = 'auto-potion';
                        //         if (currentTask.specialAlerts.has(message)) {
                        //             const existing = currentTask.specialAlerts.get(message);
                        //             existing.count++;
                        //         } else {
                        //             currentTask.specialAlerts.set(message, { type: type, count: 1 });
                        //         }
                        //     }
                        // }
                        // if (line.includes('切换盾、回血角色，使用元素战技')) {
                        //     const message = `生存位技能`;
                        //     const type = 'auto-potion';
                        //     if (currentTask.specialAlerts.has(message)) {
                        //         const existing = currentTask.specialAlerts.get(message);
                        //         existing.count++;
                        //     } else {
                        //         currentTask.specialAlerts.set(message, { type: type, count: 1 });
                        //     }
                        // }

                        // 1. 开始自动领取派遣任务！
                        if (line.includes("开始自动领取派遣任务！")) {
                            const message = '领取派遣任务';
                            const type = 'dispatch-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 2. 自动吃药
                        if (line.includes('检测到红血且不在CD，自动吃药')) {
                            const message = '自动吃药';
                            const type = 'auto-potion';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 3. 快捷键触发暂停，等待解除
                        if (line.includes("快捷键触发暂停，等待解除")) {
                            const message = '快捷键暂停';
                            const type = 'pause-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 4. 文件找不到
                        if (line.includes('"Could not find file')) {
                            const message = '路径文件丢失';
                            const type = 'file-missing';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 5. 战斗策略错误
                        if (line.includes('执行地图追踪时候发生错误: "战斗策略脚本中出现未知的方法：')) {
                            const match = line.match(/执行地图追踪时候发生错误: "战斗策略脚本中出现未知的方法：(.+?)"/);
                            if (match) {
                                const message = `地图追踪战斗策略错误："${match[1]}"`;
                                const type = 'strategy-error';
                                if (currentTask.specialAlerts.has(message)) {
                                    const existing = currentTask.specialAlerts.get(message);
                                    existing.count++;
                                } else {
                                    currentTask.specialAlerts.set(message, { type: type, count: 1 });
                                }
                            }
                        }

                        // 6. 战斗脚本设置错误
                        if (line.includes('执行脚本时发生异常: "未匹配到任何战斗脚本"')) {
                            const message = '未匹配到任何战斗脚本';
                            const type = 'combat-script-error';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 7. 缺少元素采集角色
                        if (line.includes('此路径存在 "') && line.includes('元素采集 动作，')) {
                            const match = line.match(/此路径存在 "(.+?)"元素采集 动作，/);
                            if (match) {
                                const message = `队伍缺少 ${match[1]} 元素采集角色`;
                                const type = 'element-missing';
                                if (currentTask.specialAlerts.has(message)) {
                                    const existing = currentTask.specialAlerts.get(message);
                                    existing.count++;
                                } else {
                                    currentTask.specialAlerts.set(message, { type: type, count: 1 });
                                }
                            }
                        }

                        // 8. 缺少纳西妲
                        if (line.includes('此路径存在纳西妲收集动作，队伍中没有纳西妲角色，无法执行此路径！')) {
                            const message = '队伍缺少纳西妲';
                            const type = 'nahida-missing';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 9. 切换队伍失败
                        if (line.includes('切换队伍失败，无法执行此路径！请检查配置组中的地图追踪配置！')) {
                            const message = '切换队伍失败';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 10. 当前获取焦点的窗口不是原神，尝试恢复窗口
                        if (line.includes("当前获取焦点的窗口不是原神，尝试恢复窗口")) {
                            const message = '窗口焦点异常，尝试恢复';
                            const type = 'pause-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        //10.5
                        if (line.includes("当前获取焦点的窗口不是原神，暂停")) {
                            const message = '窗口焦点异常，暂停';
                            const type = 'pause-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 11. 队伍角色识别失败
                        if (line.includes("队伍角色识别失败")) {
                            const message = '队伍角色识别失败';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 12. 打开大地图失败，请检查按键绑定中「打开地图」按键设置是否和原神游戏中一致！
                        if (line.includes("打开大地图失败，重试 3 次")) {
                            const message = '打开地图失败';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 13. 选项列表不存在传送点  重试
                        if (line.includes("选项列表不存在传送点  重试")) {
                            const message = '传送点不存在';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 14. 选项列表不存在传送点  重试
                        if (line.includes("识别大地图位置失败")) {
                            const message = '识别大地图位置失败';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 15. 切换区域失败：".*"
                        if (line.includes('切换区域失败："')) {
                            const match = line.match(/切换区域失败："(.+?)"/);
                            if (match) {
                                const message = `切换区域失败："${match[1]}"`;
                                const type = 'pause-alert';
                                if (currentTask.specialAlerts.has(message)) {
                                    const existing = currentTask.specialAlerts.get(message);
                                    existing.count++;
                                } else {
                                    currentTask.specialAlerts.set(message, { type: type, count: 1 });
                                }
                            }
                        }

                        // 16. 执行地图追踪时候发生错误: "未能返回主界面"
                        if (line.includes('执行地图追踪时候发生错误: "未能返回主界面"')) {
                            const message = '未能返回主界面';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 17. 调度器任务出现未预期的异常，自动重启bgi
                        if (line.includes('调度器任务出现未预期的异常，自动重启bgi')) {
                            const message = '自动重启bgi';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        // 99. 此追踪脚本未正常走完！
                        if (line.includes('此追踪脚本未正常走完！')) {
                            const message = '脚本未正常走完';
                            const type = 'abnormal-alert';
                            if (currentTask.specialAlerts.has(message)) {
                                const existing = currentTask.specialAlerts.get(message);
                                existing.count++;
                            } else {
                                currentTask.specialAlerts.set(message, { type: type, count: 1 });
                            }
                        }

                        const pickMatch = line.match(/交互或拾取："(.+?)"/);

                        if (pickMatch) {
                            currentTask.picks.set(pickMatch[1], (currentTask.picks.get(pickMatch[1]) || 0) + 1);
                            currentGroup.picks.set(pickMatch[1], (currentGroup.picks.get(pickMatch[1]) || 0) + 1);
                        }
                    }
                }
                prevLine = line;
            });
        }

        function finalizeParsing() {
            // 处理未完成任务
            parsingContext.activeTasks.forEach(task => {
                task.endTime = task.startTime;
            });

            // 处理未完成配置组
            parsingContext.activeGroups.forEach(group => {
                group.endTime = group.tasks.length > 0
                    ? group.tasks[group.tasks.length - 1].startTime
                    : group.startTime;
                parsingContext.allGroups.push(group);
            });

            return parsingContext.allGroups.sort((a, b) =>
                (b.endTime || b.startTime) - (a.endTime || a.startTime)
            );
        }

        function formatDuration(seconds) {
            if (seconds < 0) return "时间异常"; // 处理负值情况

            const days = Math.floor(seconds / 86400);
            seconds %= 86400;
            const hours = Math.floor(seconds / 3600);
            seconds %= 3600;
            const minutes = Math.floor(seconds / 60);
            const sec = seconds % 60;

            let result = [];
            if (days > 0) result.push(`${days}天`);
            if (hours > 0) result.push(`${hours}小时`);
            if (minutes > 0) result.push(`${minutes}分钟`);
            if (sec > 0 || result.length === 0) {
                result.push(sec % 1 === 0 ? `${sec}秒` : `${sec.toFixed(2)}秒`);
            }

            return result.join('') || "0秒";
        }

        // 新增耗时计算函数
        function calculateDuration(start, end) {
            if (!start || !end) return '进行中';
            const diff = (end.getTime() - start.getTime()) / 1000;
            return formatDuration(diff);
        }

        // 新增配置组耗时计算
        function calculateGroupDuration(group) {
            if (!group.startTime || !group.endTime) return '进行中';
            const diff = (group.endTime.getTime() - group.startTime.getTime()) / 1000;
            return formatDuration(diff);
        }

        // 新的耗时计算函数
        function formatDuration(totalSeconds) {
            if (totalSeconds <= 0) return "0秒";

            const hours = Math.floor(totalSeconds / 3600);
            const minutes = Math.floor((totalSeconds % 3600) / 60);
            const seconds = totalSeconds % 60;

            let result = "";

            if (hours > 0) {
                result += `${hours}小时`;
            }

            if (minutes > 0 || hours > 0) {
                result += `${minutes}分钟`;
            }

            if (seconds > 0 || (hours === 0 && minutes === 0)) {
                if (seconds % 1 === 0) {
                    result += `${Math.floor(seconds)}秒`;
                } else {
                    result += `${seconds.toFixed(2)}秒`;
                }
            }

            return result || "0秒";
        }

        // 时间解析辅助函数
        function parseTime(line, fileDate) {
            const timeMatch = line?.match(/\[(\d{2}):(\d{2}):(\d{2})\.\d+\]/);
            if (!timeMatch) return null;

            // 创建基础时间对象
            const baseDate = new Date(fileDate);
            const hours = parseInt(timeMatch[1]);
            const minutes = parseInt(timeMatch[2]);
            const seconds = parseInt(timeMatch[3]);

            // 智能日期推算
            const currentTime = new Date(baseDate);
            currentTime.setHours(hours, minutes, seconds);

            // 处理跨日情况
            if (lastValidTime && currentTime < lastValidTime) {
                // 如果时间回退超过4小时，视为次日（兼容凌晨4点刷新机制）
                if ((lastValidTime - currentTime) > 4 * 60 * 60 * 1000) {
                    currentTime.setDate(currentTime.getDate() + 1);
                }
            }

            lastValidTime = currentTime;
            return currentTime;
        }

        // 重写全屏功能 - 使用更可靠的方法
        function toggleFullScreen() {
            const doc = window.document;
            const docEl = doc.documentElement;

            // 检查当前全屏状态
            const isFullScreen =
                doc.fullscreenElement ||
                doc.webkitFullscreenElement ||
                doc.mozFullScreenElement ||
                doc.msFullscreenElement;

            // 切换全屏状态
            if (!isFullScreen) {
                // 进入全屏
                if (docEl.requestFullscreen) {
                    docEl.requestFullscreen();
                } else if (docEl.mozRequestFullScreen) { // Firefox
                    docEl.mozRequestFullScreen();
                } else if (docEl.webkitRequestFullscreen) { // Chrome, Safari and Opera
                    docEl.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
                } else if (docEl.msRequestFullscreen) { // IE/Edge
                    docEl.msRequestFullscreen();
                }
            } else {
                // 退出全屏
                if (doc.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.mozCancelFullScreen) { // Firefox
                    document.mozCancelFullScreen();
                } else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera
                    document.webkitExitFullscreen();
                } else if (document.msExitFullscreen) { // IE/Edge
                    document.msExitFullscreen();
                }
            }
        }

        // 切換全屏模式
        function changeFullScreen() {
            // 判断当前是否全屏
            if (!document.fullscreenElement) {
                // 进入全屏（兼容不同浏览器）
                const element = document.documentElement;
                if (element.requestFullscreen) {
                    element.requestFullscreen().catch(err => {
                        console.error(`全屏请求错误: ${err.message}`);
                    });
                } else if (element.mozRequestFullScreen) { // Firefox
                    element.mozRequestFullScreen();
                } else if (element.webkitRequestFullscreen) { // Chrome/Safari
                    element.webkitRequestFullscreen();
                } else if (element.msRequestFullscreen) { // IE/Edge
                    element.msRequestFullscreen();
                }
            } else {
                // 退出全屏（兼容不同浏览器）
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.mozCancelFullScreen) { // Firefox
                    document.mozCancelFullScreen();
                } else if (document.webkitExitFullscreen) { // Chrome/Safari
                    document.webkitExitFullscreen();
                } else if (document.msExitFullscreen) { // IE/Edge
                    document.msExitFullscreen();
                }
            }
        }

        // HTML生成逻辑
        function generateHTML(groups) {
            if (useLegacyFormat) {
                return generateLegacyFormatHTML(groups);
            } else {
                return generateNewFormatHTML(groups);
            }
        }

        // 旧版格式HTML生成
        function generateNewFormatHTML(groups) {
            let html = '<div style="width: 100%;">';

            groups.forEach((group, index) => {
                html += `
            <div class="group-container">
            <div class="group-header" data-group-index="${index}">
                <span class="arrow collapsed">▶</span>
                <div class="header-content">
                <h2>配置組：${group.name} (${formatDateTime(group.startTime)} - ${group.endTime ? `${formatDateTime(group.endTime)}) <span style="font-size:14px;color:#666;margin-left:10px;">耗时${calculateGroupDuration(group)}</span>` : '进行中)'}</h2>                    
                </div>
            </div>
            <div class="collapsible" id="group-${index}">
            <table>
                <thead>`;

                // 添加隊伍角色行到thead
                if (group.teamRoles && group.teamRoles.size > 0) {
                    html += `
                        <tr class="team-roles-row">
                            <td colspan="${useLegacyFormat ? 10 : 11}">
                                <div class="team-roles-content">
                                    ${Array.from(group.teamRoles).map(roleStr => {
                        let displayText = `队伍角色: ${roleStr}`;

                        // 添加角色類型標注
                        const roleNotes = [];

                        // 檢查行走位
                        if (group.walkRoles && group.walkRoles.length > 0) {
                            const walkRoleNames = roleStr.split(',').map(r => r.trim());
                            const foundWalkRoles = group.walkRoles.filter(walkRole =>
                                walkRoleNames.includes(walkRole)
                            );
                            if (foundWalkRoles.length > 0) {
                                roleNotes.push(`行走位:${foundWalkRoles.join(',')}`);
                            }
                        }

                        // 檢查生存位
                        if (group.survivalRoles && group.survivalRoles.length > 0) {
                            const survivalRoleNames = roleStr.split(',').map(r => r.trim());
                            const foundSurvivalRoles = group.survivalRoles.filter(survivalRole =>
                                survivalRoleNames.includes(survivalRole)
                            );
                            if (foundSurvivalRoles.length > 0) {
                                roleNotes.push(`生存位:${foundSurvivalRoles.join(',')}`);
                            }
                        }

                        // 如果有標注，添加到顯示文本
                        if (roleNotes.length > 0) {
                            displayText += `   (${roleNotes.join(' ')})`;
                        }

                        return `<div class="team-role-line">${displayText}</div>`;
                    }).join('')}
                                </div>
                            </td>
                        </tr>`;
                }

                // 添加表頭行到thead
                html += `
                    <tr class="sticky-header">
                        <th data-sort-type="string">任务名称</th>
                        <th class="column-start-time">开始时间</th>
                        <th class="column-end-time">结束时间</th>
                        <th data-sort-type="number">耗时</th>
                        <th class="column-revive">复活次数</th>
                        <th class="column-retry">重试次数</th>
                        <th class="column-retry-detail">疑似卡死</th>
                        <th class="column-too-far-skip">过远跳过</th>
                        <th class="column-timeout">战斗超时</th>
                        <th class="column-teleport">传送失败</th>
                        <th class="column-picks">拾取物</th>
                    </tr>
                </thead>
                <tbody>`;

                group.tasks.forEach(task => {

                    const hasCoords = task.faults.coords && task.faults.coords.length > 0;

                    // 生成复活事件弹窗
                    let revivePopupHTML = '';
                    let hasReviveEvents = false;
                    if (task.reviveEvents && task.reviveEvents.length > 0) {
                        hasReviveEvents = true;
                        revivePopupHTML = '<div class="battle-popup">';
                        revivePopupHTML += '<div class="battle-popup-header">';
                        revivePopupHTML += '<div class="battle-popup-title">复活事件详细</div>';
                        revivePopupHTML += '<span class="close-btn">×</span>';
                        revivePopupHTML += '</div>';
                        revivePopupHTML += '<div class="battle-popup-content">';

                        // 按battleCount分组
                        const eventsByBattleCount = {};
                        task.reviveEvents.forEach(event => {
                            if (!eventsByBattleCount[event.battleCount]) {
                                eventsByBattleCount[event.battleCount] = [];
                            }
                            eventsByBattleCount[event.battleCount].push(event);
                        });

                        // 处理战斗前的复活事件（battleCount为0）
                        if (eventsByBattleCount[0] && eventsByBattleCount[0].length > 0) {
                            eventsByBattleCount[0].forEach(event => {
                                const coordText = event.coord ? `(X=${event.coord.x}, Y=${event.coord.y})` : '';
                                revivePopupHTML += `<div class="battle-item">${coordText}</div>`;
                            });
                        }

                        // 生成完整的战斗序列（从1到最大战斗次数）
                        for (let i = 1; i <= task.maxBattleCount; i++) {
                            const events = eventsByBattleCount[i] || [];

                            if (events.length > 0) {
                                // 有复活事件，显示坐标
                                const coords = events.map(e => e.coord ? `(X=${e.coord.x}, Y=${e.coord.y})` : '').filter(text => text);
                                const coordText = coords.length > 0 ? '：' + coords.join('、') : '';
                                revivePopupHTML += `<div class="battle-item">${i}${coordText}</div>`;
                            } else {
                                // 无复活事件，只显示序号
                                revivePopupHTML += `<div class="battle-item">${i}</div>`;
                            }
                        }

                        revivePopupHTML += '</div></div>';
                    }

                    // 生成坐标弹窗HTML
                    let coordPopupHTML = '';
                    if (hasCoords) {
                        coordPopupHTML = '<div class="coord-popup">';
                        coordPopupHTML += '<div class="coord-popup-header">';
                        coordPopupHTML += '<div class="coord-popup-title">卡死点坐标</div>';
                        coordPopupHTML += '<span class="close-btn">×</span>';
                        coordPopupHTML += '</div>';
                        coordPopupHTML += '<div class="coord-popup-content">';

                        task.faults.coords.forEach((coord, idx) => {
                            coordPopupHTML += `<div class="coord-item">`;
                            coordPopupHTML += `<strong>#${idx + 1}</strong><br>`;
                            coordPopupHTML += `X: ${coord.x}<br>Y: ${coord.y}`;
                            coordPopupHTML += '</div>';
                        });

                        coordPopupHTML += '</div></div>';
                    }

                    // 生成超时事件弹窗
                    let timeoutPopupHTML = '';
                    let hasTimeoutEvents = false;
                    if (task.timeoutEvents && task.timeoutEvents.length > 0) {
                        hasTimeoutEvents = true;
                        timeoutPopupHTML = '<div class="battle-popup">';
                        timeoutPopupHTML += '<div class="battle-popup-header">';
                        timeoutPopupHTML += '<div class="battle-popup-title">超时事件详细</div>';
                        timeoutPopupHTML += '<span class="close-btn">×</span>';
                        timeoutPopupHTML += '</div>';
                        timeoutPopupHTML += '<div class="battle-popup-content">';

                        // 生成完整的战斗序列（从1到最大战斗次数）
                        for (let i = 1; i <= task.maxBattleCount; i++) {
                            const hasEvent = task.timeoutEvents.includes(i);
                            timeoutPopupHTML += `<div class="battle-item">${i}${hasEvent ? '(X)' : ''}</div>`;
                        }

                        timeoutPopupHTML += '</div></div>';
                    }

                    // 设置单元格类 - 只在有事件时添加可点击类
                    const reviveCellClass = hasReviveEvents ? 'has-battle-events' : 'no-events';
                    const timeoutCellClass = hasTimeoutEvents ? 'has-battle-events' : 'no-events';

                    // 判断是否需要将任务名和耗时标红
                    const shouldHighlight =
                        task.faults.retry > 0 &&
                        task.faults.retryDetail === 0 &&
                        task.faults.revive === 0;

                    // 格式化revive值：整数显示整数，小数显示两位小数
                    let reviveDisplay = '';
                    if (task.faults.revive !== 0) {
                        if (Number.isInteger(task.faults.revive)) {
                            reviveDisplay = task.faults.revive;
                        } else {
                            // 确保小数部分有两位
                            reviveDisplay = task.faults.revive.toFixed(2);
                        }
                    }

                    // 计算任务耗时
                    const duration = calculateDuration(task.startTime, task.endTime);

                    // 单元格类 - 如果有坐标数据添加 has-coords 类
                    const cellClass = hasCoords ? 'coords-cell has-coords' : 'coords-cell';

                    // 生成拾取物显示文本
                    let picksText = formatPicks(task.picks);

                    // 按顺序添加提示文本
                    const alerts = [];

                    // 特殊事件
                    if (task.specialAlerts && task.specialAlerts.size > 0) {
                        for (let [message, alertInfo] of task.specialAlerts) {
                            const displayMessage = alertInfo.count > 1 ? `${message} (${alertInfo.count})` : message;
                            alerts.push(`<span class="${alertInfo.type}">${displayMessage}</span>`);
                        }
                    }

                    // 如果有提示，添加到拾取物文本
                    if (alerts.length > 0) {
                        if (picksText === '无') {
                            picksText = "无<br>" + alerts.join('<br>');
                        } else {
                            picksText += '<br>' + alerts.join('<br>');
                        }
                    }

                    html += `
                <tr>
                    <td class="${shouldHighlight ? 'task-name-red' : ''}">${task.name}</td>
                    <td class="column-start-time">${formatDateTime(task.startTime)}</td>
                    <td class="column-end-time">${task.endTime ? formatDateTime(task.endTime) : '进行中'}</td>
                    <td class="${shouldHighlight ? 'duration-red' : ''}">${duration}</td>
                    <td class="column-revive ${reviveCellClass} ${task.faults.revive >= 2 ? 'red' : ''}">
                        ${reviveDisplay}
                        ${revivePopupHTML}
                    </td>
                    <td class="column-retry ${task.faults.retry >= 2 ? 'red' : ''}">${task.faults.retry == 0 ? '' : task.faults.retry}</td>
                    <td class="column-retry-detail ${cellClass} ${task.faults.retryDetail >= 2 ? '' : ''}">
                        ${task.faults.retryDetail == 0 ? '' : task.faults.retryDetail}
                        ${coordPopupHTML}
                    </td>
                    <td class="column-too-far-skip ${task.faults.too_far_skip >= 2 ? 'red' : 'red'}">${task.faults.too_far_skip == 0 ? '' : task.faults.too_far_skip}</td>
                    <td class="column-timeout ${timeoutCellClass} ${task.faults.timeout >= 1 ? 'red' : ''}">
                        ${task.faults.timeout == 0 ? '' : task.faults.timeout}
                        ${timeoutPopupHTML}
                    </td>
                    <td class="column-teleport ${task.faults.teleport >= 3 ? 'red' : ''}">${task.faults.teleport == 0 ? '' : task.faults.teleport}</td>
                        <td class="column-picks">${picksText}</td>
                    </tr>`;
                });

                html += `
                    <tr class="ignore-sort summary-row">
                        <td colspan="11" class="column-picks">
                            <span class="picks-bold">所有拾取物: </span>
                            ${formatPicks(group.picks)}
                        </td>
                    </tr>
                </tbody>
            </table>
            </div>
            </div>`;
            });

            html += '</div>';
            return html;
        }

        // 新版格式HTML生成
        function generateLegacyFormatHTML(groups) {
            let html = '<div class="legacy-format" style="width: 100%;">';

            groups.forEach((group, index) => {
                html += `
            <div class="group-container">
            <div class="group-header" data-group-index="${index}">
                <span class="arrow collapsed">▶</span>
                <div class="header-content">
                <h2>配置組：${group.name} (${formatDateTime(group.startTime)} - ${group.endTime ? `${formatDateTime(group.endTime)}) <span style="font-size:14px;color:#666;margin-left:10px;">耗时${calculateGroupDuration(group)}</span>` : '进行中)'}</h2>
                </div>
            </div>
            <div class="collapsible" id="group-${index}">
            <div class="sticky-table">
            <table>
                <thead>`;

                // 添加隊伍角色行到thead
                if (group.teamRoles && group.teamRoles.size > 0) {
                    html += `
                        <tr class="team-roles-row">
                            <td colspan="${useLegacyFormat ? 10 : 11}">
                                <div class="team-roles-content">
                                    ${Array.from(group.teamRoles).map(roleStr => {
                        let displayText = `队伍角色: ${roleStr}`;

                        // 添加角色類型標注
                        const roleNotes = [];

                        // 檢查行走位
                        if (group.walkRoles && group.walkRoles.length > 0) {
                            const walkRoleNames = roleStr.split(',').map(r => r.trim());
                            const foundWalkRoles = group.walkRoles.filter(walkRole =>
                                walkRoleNames.includes(walkRole)
                            );
                            if (foundWalkRoles.length > 0) {
                                roleNotes.push(`行走位:${foundWalkRoles.join(',')}`);
                            }
                        }

                        // 檢查生存位
                        if (group.survivalRoles && group.survivalRoles.length > 0) {
                            const survivalRoleNames = roleStr.split(',').map(r => r.trim());
                            const foundSurvivalRoles = group.survivalRoles.filter(survivalRole =>
                                survivalRoleNames.includes(survivalRole)
                            );
                            if (foundSurvivalRoles.length > 0) {
                                roleNotes.push(`生存位:${foundSurvivalRoles.join(',')}`);
                            }
                        }

                        // 如果有標注，添加到顯示文本
                        if (roleNotes.length > 0) {
                            displayText += `   (${roleNotes.join(' ')})`;
                        }

                        return `<div class="team-role-line">${displayText}</div>`;
                    }).join('')}
                                </div>
                            </td>
                        </tr>`;
                }

                html += `
                    <tr class="sticky-header">
                        <th data-sort-type="string">任务名称</th>
                        <th class="column-start-time" data-sort-type="date">开始时间</th>
                        <th class="column-end-time" data-sort-type="date">结束时间</th>
                        <th data-sort-type="number">耗时</th>
                        <th class="column-revive" data-sort-type="number">复活次数</th>
                        <th class="column-retry" data-sort-type="number">重试次数</th>
                        <th class="column-retry-detail" data-sort-type="number">疑似卡死</th>
                        <th class="column-too-far-skip" data-sort-type="number">过远跳过</th>
                        <th class="column-timeout" data-sort-type="number">战斗超时</th>
                        <th class="column-teleport" data-sort-type="number">传送失败</th>
                    </tr>
                </thead>
                <tbody>`;

                group.tasks.forEach((task, taskIndex) => {
                    const hasCoords = task.faults.coords && task.faults.coords.length > 0;

                    // 生成复活事件弹窗
                    let revivePopupHTML = '';
                    let hasReviveEvents = false;
                    if (task.reviveEvents && task.reviveEvents.length > 0) {
                        hasReviveEvents = true;
                        revivePopupHTML = '<div class="battle-popup">';
                        revivePopupHTML += '<div class="battle-popup-header">';
                        revivePopupHTML += '<div class="battle-popup-title">复活事件详细</div>';
                        revivePopupHTML += '<span class="close-btn">×</span>';
                        revivePopupHTML += '</div>';
                        revivePopupHTML += '<div class="battle-popup-content">';

                        // 按battleCount分组
                        const eventsByBattleCount = {};
                        task.reviveEvents.forEach(event => {
                            if (!eventsByBattleCount[event.battleCount]) {
                                eventsByBattleCount[event.battleCount] = [];
                            }
                            eventsByBattleCount[event.battleCount].push(event);
                        });

                        // 处理战斗前的复活事件（battleCount为0）
                        if (eventsByBattleCount[0] && eventsByBattleCount[0].length > 0) {
                            eventsByBattleCount[0].forEach(event => {
                                const coordText = event.coord ? `(X=${event.coord.x}, Y=${event.coord.y})` : '';
                                revivePopupHTML += `<div class="battle-item">${coordText}</div>`;
                            });
                        }

                        // 生成完整的战斗序列（从1到最大战斗次数）
                        for (let i = 1; i <= task.maxBattleCount; i++) {
                            const events = eventsByBattleCount[i] || [];

                            if (events.length > 0) {
                                // 有复活事件，显示坐标
                                const coords = events.map(e => e.coord ? `(X=${e.coord.x}, Y=${e.coord.y})` : '').filter(text => text);
                                const coordText = coords.length > 0 ? '：' + coords.join('、') : '';
                                revivePopupHTML += `<div class="battle-item">${i}${coordText}</div>`;
                            } else {
                                // 无复活事件，只显示序号
                                revivePopupHTML += `<div class="battle-item">${i}</div>`;
                            }
                        }

                        revivePopupHTML += '</div></div>';
                    }

                    // 生成坐标弹窗HTML
                    let coordPopupHTML = '';
                    if (hasCoords) {
                        coordPopupHTML = '<div class="coord-popup">';
                        coordPopupHTML += '<div class="coord-popup-header">';
                        coordPopupHTML += '<div class="coord-popup-title">卡死点坐标</div>';
                        coordPopupHTML += '<span class="close-btn">×</span>';
                        coordPopupHTML += '</div>';
                        coordPopupHTML += '<div class="coord-popup-content">';

                        task.faults.coords.forEach((coord, idx) => {
                            coordPopupHTML += `<div class="coord-item">`;
                            coordPopupHTML += `<strong>#${idx + 1}</strong><br>`;
                            coordPopupHTML += `X: ${coord.x}<br>Y: ${coord.y}`;
                            coordPopupHTML += '</div>';
                        });

                        coordPopupHTML += '</div></div>';
                    }

                    // 生成超时事件弹窗
                    let timeoutPopupHTML = '';
                    let hasTimeoutEvents = false;
                    if (task.timeoutEvents && task.timeoutEvents.length > 0) {
                        hasTimeoutEvents = true;
                        timeoutPopupHTML = '<div class="battle-popup">';
                        timeoutPopupHTML += '<div class="battle-popup-header">';
                        timeoutPopupHTML += '<div class="battle-popup-title">超时事件详细</div>';
                        timeoutPopupHTML += '<span class="close-btn">×</span>';
                        timeoutPopupHTML += '</div>';
                        timeoutPopupHTML += '<div class="battle-popup-content">';

                        // 生成完整的战斗序列（从1到最大战斗次数）
                        for (let i = 1; i <= task.maxBattleCount; i++) {
                            const hasEvent = task.timeoutEvents.includes(i);
                            timeoutPopupHTML += `<div class="battle-item">${i}${hasEvent ? '(X)' : ''}</div>`;
                        }

                        timeoutPopupHTML += '</div></div>';
                    }

                    // 设置单元格类 - 只在有事件时添加可点击类
                    const reviveCellClass = hasReviveEvents ? 'has-battle-events' : 'no-events';
                    const timeoutCellClass = hasTimeoutEvents ? 'has-battle-events' : 'no-events';
                    const cellClass = hasCoords ? 'coords-cell has-coords' : 'coords-cell';

                    // 判断是否需要将任务名和耗时标红
                    const shouldHighlight =
                        task.faults.retry > 0 &&
                        task.faults.retryDetail === 0 &&
                        task.faults.revive === 0;

                    // 格式化revive值：整数显示整数，小数显示两位小数
                    let reviveDisplay = '';
                    if (task.faults.revive !== 0) {
                        if (Number.isInteger(task.faults.revive)) {
                            reviveDisplay = task.faults.revive;
                        } else {
                            // 确保小数部分有两位
                            reviveDisplay = task.faults.revive.toFixed(2);
                        }
                    }

                    // 计算任务耗时
                    const duration = calculateDuration(task.startTime, task.endTime);

                    // 生成拾取物显示文本
                    let picksText = formatPicks(task.picks);

                    // 按顺序添加提示文本
                    const alerts = [];

                    // 特殊事件
                    if (task.specialAlerts && task.specialAlerts.size > 0) {
                        for (let [message, alertInfo] of task.specialAlerts) {
                            const displayMessage = alertInfo.count > 1 ? `${message} (${alertInfo.count})` : message;
                            alerts.push(`<span class="${alertInfo.type}">${displayMessage}</span>`);
                        }
                    }

                    // 如果有提示，添加到拾取物文本
                    if (alerts.length > 0) {
                        if (picksText === '无') {
                            picksText = "无<br>" + alerts.join('<br>');
                        } else {
                            picksText += '<br>' + alerts.join('<br>');
                        }
                    }

                    html += `
                <tr data-task-id="group-${index}-task-${taskIndex}">
                    <td class="main-row-name ${shouldHighlight ? 'task-name-red' : ''}" rowspan="2">${task.name}</td>
                    <td class="column-start-time">${formatDateTime(task.startTime)}</td>
                    <td class="column-end-time">${task.endTime ? formatDateTime(task.endTime) : '进行中'}</td>
                    <td class="${shouldHighlight ? 'duration-red' : ''}">${duration}</td>
                    <td class="column-revive ${reviveCellClass} ${task.faults.revive >= 2 ? 'red' : ''}">
                        ${reviveDisplay}
                        ${revivePopupHTML}
                    </td>
                    <td class="column-retry ${task.faults.retry >= 2 ? 'red' : ''}">${task.faults.retry == 0 ? '' : task.faults.retry}</td>
                    <td class="column-retry-detail ${cellClass} ${task.faults.retryDetail >= 2 ? '' : ''}">
                        ${task.faults.retryDetail == 0 ? '' : task.faults.retryDetail}
                        ${coordPopupHTML}
                    </td>
                    <td class="column-too-far-skip ${task.faults.too_far_skip >= 2 ? 'red' : 'red'}">${task.faults.too_far_skip == 0 ? '' : task.faults.too_far_skip}</td>
                    <td class="column-timeout ${timeoutCellClass} ${task.faults.timeout >= 1 ? 'red' : ''}">
                        ${task.faults.timeout == 0 ? '' : task.faults.timeout}
                        ${timeoutPopupHTML}
                    </td>
                    <td class="column-teleport ${task.faults.teleport >= 3 ? 'red' : ''}">${task.faults.teleport == 0 ? '' : task.faults.teleport}</td>
                </tr>
                <tr class="sub-row" data-task-id="group-${index}-task-${taskIndex}">
                    <td colspan="9" class="column-picks"><span class="picks-bold">拾取物: </span>${picksText}</td>
                </tr>`;
                });

                html += `
                <tr class="ignore-sort summary-row">
                    <td colspan="10" class="column-picks">
                        <span class="picks-bold">所有拾取物: </span>${formatPicks(group.picks)}
                        </td>
                </tr>
                </tbody></table>
            </div>
            </div>
            </div>`;
            });

            html += '</div>';
            return html;
        }

        // 格式化日期时间
        function formatDateTime(date) {
            if (!date) return '未知时间';
            return date.toLocaleString('zh-CN', {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit'
            });
        }

        // 修改折叠功能事件处理
        document.querySelectorAll('.group-header').forEach((el, i) => {
            el.onclick = (e) => {
                const arrow = el.querySelector('.arrow');
                const content = document.getElementById(`group-${i}`);
                const container = el.closest('.group-container');

                // 获取配置组标题和内容的位置信息
                const headerRect = el.getBoundingClientRect();
                const containerRect = container.getBoundingClientRect();

                // 判断标题是否在可视区域顶部（考虑固定定位）
                const isStickyAtTop = headerRect.top <= 15 && headerRect.top >= -10;

                if (isStickyAtTop) {
                    // 计算需要滚动到的位置
                    // 让配置组标题滚动到距离顶部30px的位置
                    const targetPosition = container.offsetTop - 40;
                    const currentPosition = dropZone.scrollTop;
                    const scrollDistance = Math.abs(targetPosition - currentPosition);

                    // 计算滚动时间：根据距离，但不小于300ms，不大于2000ms，然后加上500ms的额外时间
                    const scrollDuration = Math.min(2000, Math.max(200, scrollDistance)) + 50;

                    // 使用平滑滚动
                    dropZone.scrollTo({
                        top: targetPosition,
                        behavior: 'smooth'
                    });

                    // 在滚动完成后再折叠，使用计算出的时间
                    setTimeout(() => {
                        toggleCollapseState(el, content, arrow);
                    }, scrollDuration);

                } else {
                    // 已经在顶部，直接折叠
                    toggleCollapseState(el, content, arrow);
                }
            };
        });

        // 提取折叠状态切换逻辑到单独函数
        function toggleCollapseState(el, content, arrow) {
            // 切换状态
            el.classList.toggle('collapsed');
            content.classList.toggle('collapsed');
            arrow.classList.toggle('collapsed');

            // 使用防抖版本 折叠/展开后重新计算表头位置
            debouncedAdjustTableHeader();
        }

        // 格式化拾取物
        function formatPicks(picks) {
            if (picks.size === 0) return '无';
            return Array.from(picks.entries())
                .sort((a, b) => b[1] - a[1]) // 按数量从多到少排序
                .map(([item, count]) => `${item} (${count})`)
                .join(', ');
        }

        // 新增：更新时间列的显示状态
        function updateTimeColumnsVisibility() {
            if (timeColumnsVisible) {
                document.body.classList.remove('time-columns-hidden');
            } else {
                document.body.classList.add('time-columns-hidden');
            }
        }

        // 新增：显示时间列状态提示
        function showTimeToggleIndicator() {
            const indicator = document.getElementById('timeToggleIndicator');
            indicator.textContent = `时间列: ${timeColumnsVisible ? '显示' : '隐藏'}`;
            indicator.classList.add('visible');

            // 2秒后隐藏提示
            setTimeout(() => {
                indicator.classList.remove('visible');
            }, 2000);
        }

        // 新增：更新统计列的显示状态
        function updateStatsColumnsVisibility() {
            if (statsColumnsVisible) {
                document.body.classList.remove('stats-columns-hidden');
            } else {
                document.body.classList.add('stats-columns-hidden');
            }
        }

        // 新增：显示统计列状态提示
        function showStatsToggleIndicator() {
            const indicator = document.getElementById('statsToggleIndicator');
            indicator.textContent = `统计列: ${statsColumnsVisible ? '显示' : '隐藏'}`;
            indicator.classList.add('visible');

            // 2秒后隐藏提示
            setTimeout(() => {
                indicator.classList.remove('visible');
            }, 2000);
        }

        // 添加拾取物列显示状态更新函数
        function updatePicksColumnsVisibility() {
            if (picksColumnsVisible) {
                document.body.classList.remove('picks-columns-hidden');
            } else {
                document.body.classList.add('picks-columns-hidden');
            }
        }

        // 添加拾取物列状态提示函数
        function showPicksToggleIndicator() {
            const indicator = document.getElementById('picksToggleIndicator');
            indicator.textContent = `拾取物列: ${picksColumnsVisible ? '显示' : '隐藏'}`;
            indicator.classList.add('visible');

            // 2秒后隐藏提示
            setTimeout(() => {
                indicator.classList.remove('visible');
            }, 2000);
        }

        // 新增：更新表格格式
        function updateTableFormat() {
            if (useLegacyFormat) {
                document.body.classList.add('legacy-format');
            } else {
                document.body.classList.remove('legacy-format');
            }
        }

        // 新增：显示表格格式状态提示
        function showFormatToggleIndicator() {
            const indicator = document.getElementById('formatToggleIndicator');
            indicator.textContent = `表格格式: ${useLegacyFormat ? '新版' : '旧版'}`;
            indicator.classList.add('visible');

            // 2秒后隐藏提示
            setTimeout(() => {
                indicator.classList.remove('visible');
            }, 2000);
        }

        function refreshHTML() {
            const result = parsingContext.allGroups;

            // 保存当前状态
            const currentHideZeroStats = hideZeroStatsTasks;
            const currentFilterMode = isFilterMode;
            currentFilterKeywords = [...currentFilterKeywords];

            dropZone.innerHTML = generateHTML(result);
            setupCoordPopups();

            // 修改折叠功能事件处理
            document.querySelectorAll('.group-header').forEach((el, i) => {
                el.onclick = (e) => {
                    const arrow = el.querySelector('.arrow');
                    const content = document.getElementById(`group-${i}`);
                    const container = el.closest('.group-container');

                    // 获取配置组标题和内容的位置信息
                    const headerRect = el.getBoundingClientRect();
                    const containerRect = container.getBoundingClientRect();

                    // 判断标题是否在可视区域顶部（考虑固定定位）
                    const isStickyAtTop = headerRect.top <= 15 && headerRect.top >= -10;

                    if (isStickyAtTop) {
                        // 计算需要滚动到的位置
                        // 让配置组标题滚动到距离顶部30px的位置
                        const targetPosition = container.offsetTop - 40;
                        const currentPosition = dropZone.scrollTop;
                        const scrollDistance = Math.abs(targetPosition - currentPosition);

                        // 计算滚动时间：根据距离，但不小于300ms，不大于2000ms，然后加上500ms的额外时间
                        const scrollDuration = Math.min(2000, Math.max(200, scrollDistance)) + 50;

                        // 使用平滑滚动
                        dropZone.scrollTo({
                            top: targetPosition,
                            behavior: 'smooth'
                        });

                        // 在滚动完成后再折叠，使用计算出的时间
                        setTimeout(() => {
                            toggleCollapseState(el, content, arrow);
                        }, scrollDuration);

                    } else {
                        // 已经在顶部，直接折叠
                        toggleCollapseState(el, content, arrow);
                    }
                };
            });

            // 应用当前显示状态
            updateTimeColumnsVisibility();
            updateStatsColumnsVisibility();
            updatePicksColumnsVisibility();
            updateTableFormat();

            // 设置行高亮效果
            setupRowHighlight();

            // 动态调整表头位置 - 添加这行
            setTimeout(() => {
                adjustTableHeaderPosition();

                dropZone.scrollTop = 0;
            }, 200); // 延迟100ms确保DOM完全渲染

            // 恢复状态
            hideZeroStatsTasks = currentHideZeroStats;
            isFilterMode = currentFilterMode;
            currentFilterKeywords = currentFilterKeywords;

            // 应用筛选状态
            if (isFilterMode) {
                applyTaskFilter();
                showFilterIndicator();
            }

            // 应用异常任务显示状态
            if (hideZeroStatsTasks) {
                setTimeout(() => {
                    if (!isFilterMode) {
                        toggleZeroStatsTasksNormal();
                    } else {
                        // 在筛选模式下应用零统计筛选
                        toggleZeroStatsTasksInFilterMode();
                    }
                    updateButtonState(74);
                    updateButtonState(72);
                }, 150);
            }

            // 重新生成HTML后，根据当前模式设置body类名
            if (isStaticMode) {
                document.body.classList.add('static-mode');
            } else {
                document.body.classList.remove('static-mode');
            }

            if (isFilterMode) {
                document.body.classList.add('filter-mode');
            } else {
                document.body.classList.remove('filter-mode');
            }
        }

        // 修改adjustTableHeaderPosition函数
        function adjustTableHeaderPosition() {
            const now = Date.now();

            // 节流控制
            if (isAdjustingTableHeader || (now - lastAdjustmentTime) < ADJUSTMENT_THROTTLE) {
                return;
            }

            isAdjustingTableHeader = true;
            lastAdjustmentTime = now;

            // 使用requestAnimationFrame优化性能
            requestAnimationFrame(() => {

                try {
                    const tables = document.querySelectorAll('table');
                    const tablesLength = tables.length;

                    for (let i = 0; i < tablesLength; i++) {
                        const table = tables[i];
                        const thead = table.querySelector('thead');
                        if (!thead) continue;

                        // 缓存DOM查询结果
                        const groupContainer = table.closest('.group-container');
                        const groupHeader = groupContainer ? groupContainer.querySelector('.group-header') : null;
                        const groupHeaderHeight = groupHeader ? groupHeader.offsetHeight : 0;

                        const teamRolesRow = thead.querySelector('.team-roles-row');
                        const headerRow = thead.querySelector('.sticky-header');

                        // 批量处理样式变更
                        if (teamRolesRow) {
                            teamRolesRow.style.top = `${groupHeaderHeight - 20}px`;
                        }

                        if (teamRolesRow && headerRow) {
                            const teamRolesHeight = teamRolesRow.offsetHeight;
                            const headerTop = groupHeaderHeight + teamRolesHeight - 20;

                            headerRow.style.top = `${headerTop}px`;

                            // 批量处理th元素
                            const headerThs = headerRow.querySelectorAll('th');
                            for (let j = 0; j < headerThs.length; j++) {
                                headerThs[j].style.top = `${headerTop}px`;
                            }
                        } else if (headerRow) {
                            const headerTop = groupHeaderHeight - 20;
                            headerRow.style.top = `${headerTop}px`;

                            if (useLegacyFormat) {
                                const headerThs = headerRow.querySelectorAll('th');
                                for (let j = 0; j < headerThs.length; j++) {
                                    headerThs[j].style.top = `${headerTop}px`;
                                }
                            }
                        }
                    }
                } catch (error) {
                    console.warn('调整表头位置时出错:', error);
                } finally {
                    isAdjustingTableHeader = false;
                }
            });
        }

        // 防抖函数
        function debounce(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func(...args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        }


        // 节流函数
        function throttle(func, limit) {
            let inThrottle;
            return function (...args) {
                if (!inThrottle) {
                    func.apply(this, args);
                    inThrottle = true;
                    setTimeout(() => inThrottle = false, limit);
                }
            };
        }

        // 创建优化后的事件处理器
        const optimizedAdjustTableHeader = throttle(adjustTableHeaderPosition, 50);
        const debouncedAdjustTableHeader = debounce(adjustTableHeaderPosition, 150);

        // 修改事件监听器
        function setupOptimizedEventListeners() {
            // 滚动事件使用节流
            window.addEventListener('scroll', optimizedAdjustTableHeader, { passive: true });

            // 调整大小事件使用防抖
            window.addEventListener('resize', debouncedAdjustTableHeader, { passive: true });

            // 移除旧的事件监听器（如果有）
            window.removeEventListener('scroll', adjustTableHeaderPosition);
            window.removeEventListener('resize', adjustTableHeaderPosition);
        }

        // 在页面加载完成后设置优化的事件监听器
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', setupOptimizedEventListeners);
        } else {
            setupOptimizedEventListeners();
        }

        // 新增：设置行高亮效果
        function setupRowHighlight() {
            const tables = document.querySelectorAll('table');

            tables.forEach(table => {
                const rows = table.querySelectorAll('tr:not(.sticky-header):not(.team-roles-row)');
                const rowsArray = Array.from(rows);

                rowsArray.forEach(row => {
                    // 使用事件委托的变体，减少事件监听器数量
                    const handler = useLegacyFormat ? createLegacyRowHandler(table) : createStandardRowHandler();

                    row.addEventListener('mouseenter', handler.enter);
                    row.addEventListener('mouseleave', handler.leave);
                });
            });
        }

        function createStandardRowHandler() {
            return {
                enter: function (e) {
                    e.currentTarget.classList.add('highlighted');
                },
                leave: function (e) {
                    e.currentTarget.classList.remove('highlighted');
                }
            };
        }

        function createLegacyRowHandler(table) {
            return {
                enter: function (e) {
                    const taskId = e.currentTarget.getAttribute('data-task-id');
                    if (taskId) {
                        const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                        relatedRows.forEach(r => r.classList.add('highlighted'));
                    }
                },
                leave: function (e) {
                    const taskId = e.currentTarget.getAttribute('data-task-id');
                    if (taskId) {
                        const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                        relatedRows.forEach(r => r.classList.remove('highlighted'));
                    }
                }
            };
        }

        // 監聽鍵盤按下
        document.onkeydown = function (e) {
            var keyNum = e;       // // 使用事件对象而不是 window.event

            if (keyNum.keyCode == 54) {
                // 6 計算原神下次60體時間
                // 呼叫視窗、要求現在體力
                var ps = prompt("計算_原神_下次 60 體力\n輸入現在_原神_體力", "");
                ps = ps.split(",")
                // 判定是否數字
                if (isFinite(ps[0])) {
                    // 是否有參數次數                        
                    if (isFinite(ps[1])) {
                        var endText = "下次_原神_60體力_時間大約為"
                        for (var i = 0; i < Number(ps[1]); i++) {
                            endText = endText + "___" + GCSRT(Number(ps[0]), 60 * (i + 1), 8);
                        }
                        // 輪出結果
                        alert(endText);
                    } else {
                        alert("下次_原神_60體力_時間大約為___" + GCSRT(Number(ps), 60, 8) + "___" + GCSRT(Number(ps), 120, 8) + "。。。。。。" + GCSRT(Number(ps), 200, 8));
                    }

                } else {
                    alert("請輸入數字");
                }

            } else if (keyNum.keyCode == 55) {
                // 7 計算原神下次40體時間
                // 呼叫視窗、要求現在體力
                var ps = prompt("計算_原神_下次 40 體力\n輸入現在_原神_體力", "");
                ps = ps.split(",")

                // 判定是否數字
                if (isFinite(ps[0])) {
                    // 是否有參數次數                        
                    if (isFinite(ps[1])) {
                        var endText = "下次_原神_40體力_時間大約為"
                        for (var i = 0; i < Number(ps[1]); i++) {
                            endText = endText + "___" + GCSRT(Number(ps[0]), 40 * (i + 1), 8);
                        }
                        // 輪出結果
                        alert(endText);
                    } else {
                        alert("下次_原神_40體力_時間大約為___" + GCSRT(Number(ps), 40, 8) + "___" + GCSRT(Number(ps), 80, 8) + "。。。。。。" + GCSRT(Number(ps), 200, 8));
                    }
                } else {
                    alert("請輸入數字");
                }
            } else if (keyNum.keyCode == 56) {
                // 8 計算自訂體力時間
                // 呼叫視窗、要求現在體力
                var ps = prompt("輸入現在_要計算體力_的參數\r(現在體力,目標體力,體力cd)", "");
                //分割為列陣
                ps = ps.split(",")
                // 判定是否數字
                if (isFinite(ps[0]) || isFinite(ps[1]) || isFinite(ps[2])) {
                    alert("下次_目標體力_時間大約為___" + GCSRT(Number(ps[0]), Number(ps[1]), Number(ps[2])));
                } else {
                    alert("請輸入數字");
                }
            } else if (keyNum.keyCode == 57) {
                // 9 計算任務距離時間
                handleDateComparison();
            } else if (keyNum.keyCode == 96 || keyNum.keyCode == 48) {
                // 左0or右0 幫助
                var help_text = "";
                help_text += "Better Genshin Impact 日志分析工具 - 简易说明\n";
                help_text += "• 鼠标悬停统计数字查看详情\n\n";

                help_text += "常用快捷键:\n";
                help_text += "T   切换时间列显示/隐藏\n";
                help_text += "Y   切换统计列显示/隐藏\n";
                help_text += "U   切换表格格式(新旧版)\n";
                help_text += "I   切换拾取物列显示/隐藏\n";
                help_text += "J   隐藏/显示所有统计为0的任务\n";
                help_text += "P   进入/退出导出模式\n";
                help_text += "~   切换亮色/暗色主题\n";
                help_text += "R   重置弹窗位置\n\n";

                help_text += "实用计算功能:\n";
                help_text += "7   计算原神下次40体力时间\n";
                help_text += "9   计算时间距离现在多久\n";
                help_text += "右2 计算日期+3天\n";
                help_text += "右3 计算两个时间点间隔\n";
                help_text += "右5 计算N天后的日期\n";
                help_text += "右7 计算原神下次120体力时间\n";
                help_text += "右8 计算HH.MM后的时间\n";
                help_text += "右9 计算时间+46小时后\n\n";
                help_text += "详细说明请复制下方网址查看:";

                // 使用prompt并预设网址，方便用户复制
                prompt(help_text, "https://github.com/this-Fish/this-fish.github.io/tree/main/Read_BetterGI_Log");

            } else if (keyNum.keyCode == 98) {
                // 計算BGI日志格式 +3天
                addThreeDaysSimple();
            } else if (keyNum.keyCode == 99) {
                // 右3 計算運行時間
                calculateTimeDifference();
            } else if (keyNum.keyCode == 101) {
                // 右5 計算+N天數
                handleAddDays();
            } else if (keyNum.keyCode == 103) {
                // 右7 計算原神下次120體時間
                // 呼叫視窗、要求現在體力
                var ps = prompt("計算_原神_下次 120 體力\n輸入現在_原神_體力", "");
                ps = ps.split(",")

                // 判定是否數字
                if (isFinite(ps[0])) {
                    // 是否有參數次數                        
                    if (isFinite(ps[1])) {
                        var endText = "下次_原神_120體力_時間大約為"
                        for (var i = 0; i < Number(ps[1]); i++) {
                            endText = endText + "___" + GCSRT(Number(ps[0]), 120 * (i + 1), 8);
                        }
                        // 輪出結果
                        alert(endText);
                    } else {
                        alert("下次_原神_120體力_時間大約為___" + GCSRT(Number(ps), 120, 8) + "。。。。。。" + GCSRT(Number(ps), 200, 8));
                    }
                } else {
                    alert("請輸入數字");
                }
            } else if (keyNum.keyCode == 104) {
                // 右8 計算任務HH.MM后時間
                handleAddCustomTime();
            } else if (keyNum.keyCode == 105) {
                // 右9 計算任務46小時后時間
                handleDateAddition();
            } else if (keyNum.keyCode == 70 && keyNum.altKey) {
                // alt +  f
                toggleFullScreen();
                e.preventDefault(); // 阻止默认行为
                return;
            } else if (keyNum.keyCode == 192) {
                document.body.classList.toggle('dark-theme'); // 切换暗色主题

                // 更新帮助提示的样式
                const helpIndicator = document.getElementById('helpIndicator');
                if (document.body.classList.contains('dark-theme')) {
                    helpIndicator.style.backgroundColor = '#fff';
                    helpIndicator.style.color = '#000';
                } else {
                    helpIndicator.style.backgroundColor = '#000';
                    helpIndicator.style.color = '#fff';
                }
            } else if (keyNum.keyCode == 87) {
                // W 畫面向上移
                dropZone.scrollBy(0, -150);
            } else if (keyNum.keyCode == 83) {
                // S 畫面向下移
                dropZone.scrollBy(0, 150);
            } else if (keyNum.keyCode == 110 || keyNum.keyCode == 190) {
                // 左. 右. 同視窗開新頁面
                window.open("index.html");
            } else if (keyNum.keyCode === 82) {
                // R键
                if (window.resetCoordPopups) {
                    window.resetCoordPopups();
                }
                e.preventDefault(); // 阻止默认行为
            } else if (keyNum.keyCode == 84) { // T键 - 切换时间列显示
                timeColumnsVisible = !timeColumnsVisible;
                updateTimeColumnsVisibility();
                showTimeToggleIndicator();
                e.preventDefault(); // 阻止默认行为
            } else if (keyNum.keyCode == 89) { // Y键 - 切换统计列显示
                statsColumnsVisible = !statsColumnsVisible;
                updateStatsColumnsVisibility();
                showStatsToggleIndicator();
                e.preventDefault(); // 阻止默认行为
            } else if (keyNum.keyCode == 85) { // U 键 - 切换表格格式
                // 检查是否有日志数据
                if (!hasLogData) {
                    e.preventDefault();
                    return;
                }

                // 保存当前状态
                const currentHideZeroStats = hideZeroStatsTasks;
                const currentFilterMode = isFilterMode;

                useLegacyFormat = !useLegacyFormat;
                refreshHTML();
                showFormatToggleIndicator();

                // 恢复状态
                hideZeroStatsTasks = currentHideZeroStats;
                isFilterMode = currentFilterMode;

                // 更新按钮状态
                updateButtonState(74); // 异常任务按钮
                updateButtonState(72); // 筛选模式按钮

                e.preventDefault(); // 阻止默认行为
            } else if (keyNum.keyCode == 72) { // H 鍵 - 任務篩選
                // 檢查是否有日志數據
                if (!hasLogData) {
                    e.preventDefault();
                    return;
                }

                toggleTaskFilter();
                e.preventDefault();
            } else if (keyNum.keyCode == 73) { // I键 - 切换拾取物列显示（仅旧版格式）
                // if (!useLegacyFormat) {
                picksColumnsVisible = !picksColumnsVisible;
                updatePicksColumnsVisibility();
                showPicksToggleIndicator();
                e.preventDefault(); // 阻止默认行为
                // }
            } else if (keyNum.keyCode == 74) { // J 键 - 隐藏/显示所有统计为0的任务
                toggleZeroStatsTasks();
                e.preventDefault(); // 阻止默认行为
            } else if (keyNum.keyCode === 80 || keyNum.key === 'p' || keyNum.key === 'P') {
                // 阻止默认行为，避免可能冲突
                e.preventDefault();

                // 处理 P 键功能
                handlePKey();
            } else if (keyNum.keyCode == 76 && keyNum.altKey) { // Alt+L
                // 如果在移动设备上，不允许隐藏
                if (document.body.classList.contains('mobile-device')) {
                    // alert('在移动设备上上传区域始终保持显示');
                    return;
                }

                // 切换电脑设备上的显示状态
                uploadContainerVisible = !uploadContainerVisible;
                const uploadContainer = document.getElementById('fileUploadContainer');

                if (uploadContainerVisible) {
                    uploadContainer.classList.add('visible');
                } else {
                    uploadContainer.classList.remove('visible');
                }


                e.preventDefault();
            }
            // 在每个快捷键处理完后，更新对应的按钮状态
            if ([192, 84, 89, 73, 80, 74].includes(keyNum.keyCode)) {
                // 稍等片刻再更新，确保状态变量已经改变
                setTimeout(() => {
                    updateButtonState(e.keyCode);
                }, 50);
            }
        }

        // 添加显示状态提示函数
        function showZeroStatsToggleIndicator() {
            const indicator = document.getElementById('zeroStatsToggleIndicator');
            indicator.textContent = `零统计任务: ${hideZeroStatsTasks ? '隐藏' : '显示'}`;
            indicator.classList.add('visible');

            // 2秒后隐藏提示
            setTimeout(() => {
                indicator.classList.remove('visible');
            }, 2000);
        }

        // 修改退出零统计筛选的逻辑
        function toggleZeroStatsTasks() {
            hideZeroStatsTasks = !hideZeroStatsTasks;

            // 如果在筛选模式下，需要重新应用筛选
            if (isFilterMode) {
                // 先应用筛选模式
                applyTaskFilter();
                // 然后应用零统计筛选 - 使用筛选模式版本
                if (hideZeroStatsTasks) {
                    toggleZeroStatsTasksInFilterMode();
                } else {
                    // 退出零统计筛选时，重新应用筛选但不隐藏零统计任务
                    applyTaskFilter();
                }
            } else {
                // 非筛选模式下正常执行
                toggleZeroStatsTasksNormal();
            }

            showZeroStatsToggleIndicator();
            // 更新按钮状态
            updateButtonState(74);
        }

        // 修改筛选模式下的零统计筛选函数
        function toggleZeroStatsTasksInFilterMode() {
            const tables = document.querySelectorAll('table');

            tables.forEach(table => {
                const container = table.closest('.group-container');
                const groupHeader = container.querySelector('.group-header h2');
                const groupName = groupHeader ? groupHeader.textContent : '';

                // 确保队伍角色行始终显示
                const teamRolesRow = table.querySelector('.team-roles-row');
                if (teamRolesRow) {
                    teamRolesRow.style.display = ''; // 确保显示
                }

                // 确保拾取物行保持隐藏
                const summaryRows = table.querySelectorAll('.summary-row');
                summaryRows.forEach(row => {
                    row.style.display = 'none';
                });

                let groupHasVisibleTask = false;

                // 第一步：检查配置组名是否匹配关键字
                const groupMatchesKeyword = currentFilterKeywords.some(keyword =>
                    groupName.toLowerCase().includes(keyword.toLowerCase())
                );

                // 如果配置组名匹配，检查组内任务并根据统计列筛选
                // (需要對舊版UI格做特殊處理)
                if (groupMatchesKeyword) {

                    if (!useLegacyFormat) {

                        // 显示所有非零统计任务
                        const rows = table.querySelectorAll('tr');
                        rows.forEach(row => {
                            // 跳过表头行、队伍角色行和summary-row
                            if (row.querySelector('th') || row.classList.contains('team-roles-row') || row.classList.contains('summary-row')) {
                                // 确保这些行显示，但summary-row保持隐藏
                                if (row.classList.contains('summary-row')) {
                                    row.style.display = 'none';
                                } else {
                                    row.style.display = '';
                                }
                                return;
                            }

                            // 检查统计列是否非零
                            let matchesStats = true;
                            if (hideZeroStatsTasks) {
                                const reviveCell = row.querySelector('.column-revive');
                                const retryCell = row.querySelector('.column-retry');
                                const retryDetailCell = row.querySelector('.column-retry-detail');
                                const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                const timeoutCell = row.querySelector('.column-timeout');
                                const teleportCell = row.querySelector('.column-teleport');

                                if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                    const isAllZero =
                                        (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                        (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                        (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                        (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                        (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                        (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                    matchesStats = !isAllZero;
                                }
                            }

                            if (matchesStats) {
                                row.style.display = '';
                                groupHasVisibleTask = true;
                            } else {
                                row.style.display = 'none';
                            }
                        });
                    } else if (useLegacyFormat) {
                        // 旧版格式
                        const taskNameRows = table.querySelectorAll('tr .main-row-name');

                        taskNameRows.forEach(taskNameCell => {
                            const row = taskNameCell.closest('tr');
                            const taskId = row.getAttribute('data-task-id');

                            if (taskId) {
                                // 检查统计列是否非零
                                let matchesStats = true;
                                if (hideZeroStatsTasks) {
                                    const reviveCell = row.querySelector('.column-revive');
                                    const retryCell = row.querySelector('.column-retry');
                                    const retryDetailCell = row.querySelector('.column-retry-detail');
                                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                    const timeoutCell = row.querySelector('.column-timeout');
                                    const teleportCell = row.querySelector('.column-teleport');

                                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                        const isAllZero =
                                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                        matchesStats = !isAllZero;
                                    }
                                }

                                if (matchesStats) {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        // 跳过summary-row
                                        if (!r.classList.contains('summary-row')) {
                                            r.style.display = '';
                                        }
                                    });
                                    groupHasVisibleTask = true;
                                } else {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        r.style.display = 'none';
                                    });
                                }
                            }
                        });
                    }

                }
                // 如果配置组名不匹配，检查组内任务
                else {
                    // 新版格式：直接扫描所有行
                    if (!useLegacyFormat) {
                        const rows = table.querySelectorAll('tr');

                        rows.forEach(row => {
                            // 跳过表头行、队伍角色行和summary-row
                            if (row.querySelector('th') || row.classList.contains('team-roles-row') || row.classList.contains('summary-row')) {
                                // 确保这些行显示，但summary-row保持隐藏
                                if (row.classList.contains('summary-row')) {
                                    row.style.display = 'none';
                                } else {
                                    row.style.display = '';
                                }
                                return;
                            }

                            // 获取任务名称
                            const taskNameCell = row.querySelector('td:first-child');
                            if (taskNameCell) {
                                const taskName = taskNameCell.textContent.toLowerCase();

                                // 检查任务名称是否包含任一关键字
                                const matchesKeyword = currentFilterKeywords.some(keyword =>
                                    taskName.includes(keyword.toLowerCase())
                                );

                                // 检查统计列是否非零
                                let matchesStats = true;
                                if (hideZeroStatsTasks) {
                                    const reviveCell = row.querySelector('.column-revive');
                                    const retryCell = row.querySelector('.column-retry');
                                    const retryDetailCell = row.querySelector('.column-retry-detail');
                                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                    const timeoutCell = row.querySelector('.column-timeout');
                                    const teleportCell = row.querySelector('.column-teleport');

                                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                        const isAllZero =
                                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                        matchesStats = !isAllZero;
                                    }
                                }

                                if (matchesKeyword && matchesStats) {
                                    row.style.display = '';
                                    groupHasVisibleTask = true;
                                } else {
                                    row.style.display = 'none';
                                }
                            }
                        });
                    }
                    // 旧版格式
                    else {
                        const taskNameRows = table.querySelectorAll('tr .main-row-name');

                        taskNameRows.forEach(taskNameCell => {
                            const row = taskNameCell.closest('tr');
                            const taskId = row.getAttribute('data-task-id');

                            if (taskId) {
                                const taskName = taskNameCell.textContent.toLowerCase();

                                // 检查任务名称是否包含任一关键字
                                const matchesKeyword = currentFilterKeywords.some(keyword =>
                                    taskName.includes(keyword.toLowerCase())
                                );

                                // 检查统计列是否非零
                                let matchesStats = true;
                                if (hideZeroStatsTasks) {
                                    const reviveCell = row.querySelector('.column-revive');
                                    const retryCell = row.querySelector('.column-retry');
                                    const retryDetailCell = row.querySelector('.column-retry-detail');
                                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                                    const timeoutCell = row.querySelector('.column-timeout');
                                    const teleportCell = row.querySelector('.column-teleport');

                                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                                        const isAllZero =
                                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                                        matchesStats = !isAllZero;
                                    }
                                }

                                if (matchesKeyword && matchesStats) {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        // 跳过summary-row
                                        if (!r.classList.contains('summary-row')) {
                                            r.style.display = '';
                                        }
                                    });
                                    groupHasVisibleTask = true;
                                } else {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        r.style.display = 'none';
                                    });
                                }
                            }
                        });
                    }
                }

                // 如果配置组没有可见任务且处于隐藏模式，隐藏整个配置组
                if (hideZeroStatsTasks && !groupHasVisibleTask) {
                    container.style.display = 'none';
                } else {
                    container.style.display = '';
                }
            });
        }


        // 新增：非筛选模式下的零统计筛选
        function toggleZeroStatsTasksNormal() {
            const tables = document.querySelectorAll('table');

            // 先显示所有配置组
            document.querySelectorAll('.group-container').forEach(container => {
                container.style.display = '';
            });

            tables.forEach(table => {
                const container = table.closest('.group-container');
                let hasVisibleTask = false;

                const rows = table.querySelectorAll('tr');

                rows.forEach(row => {
                    // 跳过表头行
                    if (row.querySelector('th')) {
                        row.style.display = '';
                        return;
                    }

                    // 检查是否为汇总行
                    if (row.classList.contains('summary-row')) {
                        row.style.display = hideZeroStatsTasks ? 'none' : '';
                        return;
                    }

                    // 获取统计单元格
                    const reviveCell = row.querySelector('.column-revive');
                    const retryCell = row.querySelector('.column-retry');
                    const retryDetailCell = row.querySelector('.column-retry-detail');
                    const tooFarSkipCell = row.querySelector('.column-too-far-skip');
                    const timeoutCell = row.querySelector('.column-timeout');
                    const teleportCell = row.querySelector('.column-teleport');

                    if (reviveCell && retryCell && retryDetailCell && tooFarSkipCell && timeoutCell && teleportCell) {
                        const isAllZero =
                            (reviveCell.textContent.trim() === '' || reviveCell.textContent.trim() === '0') &&
                            (retryCell.textContent.trim() === '' || retryCell.textContent.trim() === '0') &&
                            (retryDetailCell.textContent.trim() === '' || retryDetailCell.textContent.trim() === '0') &&
                            (tooFarSkipCell.textContent.trim() === '' || tooFarSkipCell.textContent.trim() === '0') &&
                            (timeoutCell.textContent.trim() === '' || timeoutCell.textContent.trim() === '0') &&
                            (teleportCell.textContent.trim() === '' || teleportCell.textContent.trim() === '0');

                        if (isAllZero) {
                            row.style.display = hideZeroStatsTasks ? 'none' : '';

                            // 对于旧版格式，处理关联行
                            if (useLegacyFormat) {
                                const taskId = row.getAttribute('data-task-id');
                                if (taskId) {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        r.style.display = hideZeroStatsTasks ? 'none' : '';
                                    });
                                }
                            }
                        } else {
                            hasVisibleTask = true;
                            row.style.display = '';

                            // 对于旧版格式，确保关联行显示
                            if (useLegacyFormat) {
                                const taskId = row.getAttribute('data-task-id');
                                if (taskId) {
                                    const relatedRows = table.querySelectorAll(`tr[data-task-id="${taskId}"]`);
                                    relatedRows.forEach(r => {
                                        r.style.display = '';
                                    });
                                }
                            }
                        }
                    }
                });

                // 如果配置组没有可见任务且处于隐藏模式，隐藏整个配置组
                if (hideZeroStatsTasks && !hasVisibleTask) {
                    container.style.display = 'none';
                }
            });
        }

        /**
         * 計算體力恢復完成的時間
         * calculateStaminaRecoveryTime
         * GCSRT(現在體力,原40or尘30,原8or尘6)
         * @param {number} currentStamina 當前體力值
         * @param {number} targetStamina 需要達到的目標體力值
         * @param {number} minutesPerPoint 每恢復1點體力需要的分鐘數
         * @returns {object} 包含恢復完成的時間（Date 對象）和格式化時間字符串的對象
         */
        function GCSRT(currentStamina, targetStamina, minutesPerPoint) {
            // 參數校驗
            if (typeof currentStamina !== 'number' ||
                typeof targetStamina !== 'number' ||
                typeof minutesPerPoint !== 'number') {
                throw new Error('所有參數必須是數字類型');
            }

            // 如現時體力超過目標，減過現時體力、計算下次目標體力
            if (currentStamina >= targetStamina) {
                currentStamina = currentStamina - targetStamina;
                targetStamina = targetStamina + targetStamina;
            }

            // 計算體力差
            const staminaDifference = targetStamina - currentStamina;

            // 計算總需要分鐘數
            const totalMinutes = staminaDifference * minutesPerPoint;

            // 計算恢復完成的時間
            const now = new Date(); // 當前時間
            const recoveryTime = new Date(now.getTime() + totalMinutes * 60000); // 加上總分鐘數

            // 格式化時間輸出
            const formattedTime = recoveryTime.toLocaleString(); // 根據本地時間格式顯示

            return ` ${recoveryTime.getHours()} : ${recoveryTime.getMinutes()}`;
        }

        // 計算時間差
        function handleDateComparison() {
            // 获取用户输入
            const input = prompt("請輸入日期時間（格式：YYYY/MM/DD HH:mm:ss）");

            // 验证输入是否为空
            if (input === null || input.trim() === "") {
                // alert("輸入已取消或為空！");
                console.log("輸入已取消或為空！");
                return;
            }

            // 验证格式正则表达式
            const formatRegex = /^\d{4}[-\/]\d{2}[-\/]\d{2} \d{2}:\d{2}:\d{2}$/;
            if (!formatRegex.test(input)) {
                alert("格式錯誤！請嚴格按照示例格式輸入");
                return;
            }

            // 解析日期
            const parseDate = (str) => {
                const [datePart, timePart] = str.split(" ");
                const [year, month, day] = datePart.split(/[-\/]/).map(Number); // 使用正則分割符
                const [hours, minutes, seconds] = timePart.split(":").map(Number);
                return new Date(year, month - 1, day, hours, minutes, seconds);
            };

            try {
                const targetDate = parseDate(input);
                const now = new Date();

                // 检查日期有效性
                if (isNaN(targetDate.getTime())) {
                    throw new Error("無效的日期時間");
                }

                // 计算时间差（毫秒）
                const diff = targetDate - now;

                // 格式化时间差
                const formatDuration = (ms) => {
                    const seconds = Math.floor(Math.abs(ms) / 1000);
                    const days = Math.floor(seconds / 86400);
                    const hours = Math.floor((seconds % 86400) / 3600);
                    const minutes = Math.floor((seconds % 3600) / 60);
                    const sec = seconds % 60;
                    return `${days}天 ${hours}小時 ${minutes}分鐘 ${sec}秒`;
                };

                if (diff < 0) {
                    alert(`距離 ${input} 已經過去了：\n${formatDuration(diff)}`);
                } else {
                    alert(`距離 ${input} 還有：\n${formatDuration(diff)}`);
                }
            } catch (e) {
                alert(`錯誤：${e.message}`);
            }
        }

        // BGI格式日期+3天
        function addThreeDaysSimple() {
            // 获取用户输入
            const input = prompt("請輸入日期（格式：指定日期格式）\n例如：2025/05/22 或 2025-05-22");

            // 验证输入是否为空
            if (input === null || input.trim() === "") {
                alert("輸入已取消或為空！");
                return;
            }

            // 更灵活的格式正则表达式（支持 / 和 - 分隔符）
            const formatRegex = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/;
            const formatRegex2 = /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}$/;
            if (!formatRegex.test(input) && !formatRegex2.test(input)) {
                alert("格式錯誤！請使用 YYYY/MM/DD 或 YYYY-MM-DD 格式輸入");
                return;
            }

            // 解析日期（支持 / 和 - 分隔符）
            const parseDate = (str) => {
                const separator = str.includes('/') ? '/' : '-';
                const parts = str.split(separator);
                const year = parseInt(parts[0], 10);
                const month = parseInt(parts[1], 10) - 1; // 月份从0开始
                const day = parseInt(parts[2], 10);
                return new Date(year, month, day);
            };

            try {
                const inputDate = parseDate(input);

                // 检查日期有效性
                if (isNaN(inputDate.getTime())) {
                    throw new Error("無效的日期");
                }

                // 添加3天
                const resultDate = new Date(inputDate);
                resultDate.setDate(resultDate.getDate() + 3);

                // 格式化日期函数（仅显示年月日）
                const formatDate = (date) => {
                    const year = date.getFullYear();
                    const month = String(date.getMonth() + 1).padStart(2, '0');
                    const day = String(date.getDate()).padStart(2, '0');
                    return `${year}/${month}/${day}`;
                };

                // 显示结果
                alert(
                    `原始日期：${formatDate(inputDate)}\n` +
                    `加3天後日期：${formatDate(resultDate)}`
                );

            } catch (e) {
                alert(`錯誤：${e.message}`);
            }
        }

        // 計算運行時間
        function calculateTimeDifference() {
            // 輸入格式驗證正則
            const timeFormatRegex = /^\d{4}[-\/]\d{2}[-\/]\d{2} \d{2}:\d{2}:\d{2}$/;

            // 獲取第一個時間
            const time1Str = prompt("請輸入第一個時間（格式：YYYY/MM/DD HH:mm:ss）\n範例：2025/05/22 10:52:05");
            if (!time1Str || !timeFormatRegex.test(time1Str)) {
                alert("輸入取消或格式錯誤！");
                return;
            }

            // 獲取第二個時間
            const time2Str = prompt("請輸入第二個時間（格式：YYYY/MM/DD HH:mm:ss）");
            if (!time2Str || !timeFormatRegex.test(time2Str)) {
                alert("輸入取消或格式錯誤！");
                return;
            }

            // 解析時間函數
            const parseTime = (str) => {
                const [datePart, timePart] = str.split(' ');
                const [year, month, day] = datePart.split(/[-\/]/).map(Number); // 使用正則分割符
                const [hours, minutes, seconds] = timePart.split(':').map(Number);
                return new Date(year, month - 1, day, hours, minutes, seconds);
            };

            try {
                // 解析日期對象
                const date1 = parseTime(time1Str);
                const date2 = parseTime(time2Str);

                // 驗證日期有效性
                if (isNaN(date1.getTime()) || isNaN(date2.getTime())) {
                    throw new Error("包含無效日期時間");
                }

                // 計算時間差（毫秒）
                const diffMs = date2 - date1;

                // 時間差格式化
                const formatDuration = (ms) => {
                    const absMs = Math.abs(ms);
                    const seconds = Math.floor(absMs / 1000);
                    const days = Math.floor(seconds / 86400);
                    const hours = Math.floor((seconds % 86400) / 3600);
                    const minutes = Math.floor((seconds % 3600) / 60);
                    const secs = seconds % 60;
                    return `${days}天 ${hours}小時 ${minutes}分鐘 ${secs}秒`;
                };

                // 判斷先後關係
                let orderText = '';
                if (diffMs > 0) {
                    orderText = `${time2Str} 晚於 ${time1Str}`;
                } else if (diffMs < 0) {
                    orderText = `${time2Str} 早於 ${time1Str}`;
                } else {
                    orderText = "兩個時間完全相同";
                }

                // 顯示結果
                alert(
                    `時間差計算結果：\n\n` +
                    `${orderText}\n` +
                    `相差：${formatDuration(diffMs)}`
                );

            } catch (e) {
                alert(`錯誤：${e.message}`);
            }
        }

        // 計算+N天數
        function handleAddDays() {
            // 獲取用戶輸入
            const input = prompt("請輸入要添加的天數");

            // 驗證輸入是否為空
            if (input === null || input.trim() === "") {
                alert("輸入已取消或為空！");
                return;
            }

            // 轉換為數字
            const days = parseFloat(input);
            if (isNaN(days)) {
                alert("無效的數字！請輸入有效的天數");
                return;
            }

            // 計算新日期
            const currentDate = new Date();
            const newDate = new Date(currentDate.getTime() + days * 86400000);

            // 格式化日期函數
            const formatDate = (date) => {
                const year = date.getFullYear();
                const month = String(date.getMonth() + 1).padStart(2, '0');
                const day = String(date.getDate()).padStart(2, '0');
                return `${year}/${month}/${day}`;
            };

            // 顯示結果
            alert(
                `當前時間：${formatDate(currentDate)}\n` +
                `加上 ${days} 天後的時間：${formatDate(newDate)}`
            );
        }

        // 計算任務HH.MM后時間
        function handleAddCustomTime() {
            // 獲取用戶輸入
            const input = prompt("請輸入要添加的時間（格式：hh.mm，例如 02.30）");

            // 驗證輸入是否為空
            if (input === null || input.trim() === "") {
                alert("輸入已取消或為空！");
                return;
            }

            // 驗證格式正則表達式
            const timeFormatRegex = /^-?\d{1,2}\.\d{1,2}$/;
            if (!timeFormatRegex.test(input)) {
                alert("格式錯誤！請使用 hh.mm 格式輸入（例如 03.15 或 2.5）");
                return;
            }

            // 解析時間
            const [hoursStr, minutesStr] = input.split('.');
            const hours = parseInt(hoursStr, 10);
            const minutes = parseInt(minutesStr, 10);

            // 驗證時間範圍
            if (Math.abs(hours) > 8760) { // 防止過大數值（約1年）
                alert("小時數值過大！");
                return;
            }
            if (Math.abs(minutes) > 59) {
                alert("分鐘數值需在 0-59 之間");
                return;
            }

            // 計算時間差
            const currentDate = new Date();
            const timeToAdd =
                hours * 60 * 60 * 1000 +
                minutes * 60 * 1000;

            // 計算新時間
            const newDate = new Date(currentDate.getTime() + timeToAdd);

            // 格式化日期函數（不顯示秒數）
            const formatTime = (date) => {
                const year = date.getFullYear();
                const month = String(date.getMonth() + 1).padStart(2, '0');
                const day = String(date.getDate()).padStart(2, '0');
                const hours = String(date.getHours()).padStart(2, '0');
                const mins = String(date.getMinutes()).padStart(2, '0');
                return `${year}/${month}/${day} ${hours}:${mins}`;
            };

            // 顯示結果
            alert(
                `當前時間：${formatTime(currentDate)}\n` +
                `加上 ${input.replace('.', '小時')}分鐘 後時間：${formatTime(newDate)}`
            );
        }

        // 計算46小時后
        function handleDateAddition() {
            // 獲取用戶輸入
            const input = prompt("請輸入日期時間（格式：YYYY/MM/DD HH:mm:ss）");

            // 驗證輸入是否為空
            if (input === null || input.trim() === "") {
                console.log("輸入已取消或為空！");
                return;
            }

            // 驗證格式正則表達式
            const formatRegex = /^\d{4}[\/]\d{2}[\/]\d{2} \d{2}:\d{2}:\d{2}$/;  // 嚴格匹配斜杠
            if (!formatRegex.test(input)) {
                alert("格式錯誤！請嚴格按照YYYY/MM/DD HH:mm:ss格式輸入");
                return;
            }

            // 解析日期
            const parseDate = (str) => {
                const [datePart, timePart] = str.split(" ");
                const [year, month, day] = datePart.split("/").map(Number);  // 固定使用斜杠分割
                const [hours, minutes, seconds] = timePart.split(":").map(Number);
                return new Date(year, month - 1, day, hours, minutes, seconds);
            };

            try {
                const targetDate = parseDate(input);

                // 檢查日期有效性
                if (isNaN(targetDate.getTime())) {
                    throw new Error("無效的日期時間");
                }

                // 加上46小時
                const newDate = new Date(targetDate.getTime() + 46 * 60 * 60 * 1000);

                // 格式化輸出
                const formatDate = (date) => {
                    const year = date.getFullYear();
                    const month = String(date.getMonth() + 1).padStart(2, '0');  // 補零
                    const day = String(date.getDate()).padStart(2, '0');
                    const hours = String(date.getHours()).padStart(2, '0');
                    const minutes = String(date.getMinutes()).padStart(2, '0');
                    const seconds = String(date.getSeconds()).padStart(2, '0');
                    return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
                };

                alert(
                    `原始時間：${input}\n` +
                    `加上46小時後：${formatDate(newDate)}`
                );
            } catch (e) {
                alert(`錯誤：${e.message}`);
            }
        }


    </script>
</body>

</html>
